commit 2fc8f53329caf2406870007e53110060ad3a73f1 Author: Matt Traudt sirmatt@ksu.edu Date: Tue Jun 12 21:18:13 2018 -0400
Measure exits in the exit position; try harder to find a helper --- CHANGELOG.md | 13 ++++++++++ sbws/core/scanner.py | 67 ++++++++++++++++++++++++++++++++++++++++----------- sbws/lib/relaylist.py | 20 +++++++++++++++ 3 files changed, 86 insertions(+), 14 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3097ce2..918421a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
+### Changed + +- If the relay to measure is an exit, put it in the exit position and choose a + non-exit to help. Previously the relay to measure would always be the first +hop. (GH#181) +- Try harder to find a relay to help measure the target relay with two changes. + Essentially: (1) Instead of only picking from relays that are 1.25 - 2.00 +times faster than it by consensus weight, try (in order) to find a relay that +is at least 2.00, 1.75, 1.50, 1.25, or 1.00 times as fast. If that fails, +instead of giving up, (2) pick the fastest relay in the network instead of +giving up. This compliments the previous change about measuring target exits in +the exit position. + ### Fixed
- Exception that causes sbws to fall back to one measurement thread. We first diff --git a/sbws/core/scanner.py b/sbws/core/scanner.py index 8e9f9c4..6af1b0c 100644 --- a/sbws/core/scanner.py +++ b/sbws/core/scanner.py @@ -131,6 +131,40 @@ def measure_bandwidth_to_server(session, conf, dest, content_length): return results
+def _pick_ideal_second_hop(relay, dest, rl, cont, is_exit): + ''' + Sbws builds two hop circuits. Given the **relay** to measure with + destination **dest**, pick a second relay that is or is not an exit + according to **is_exit**. + ''' + candidates = [] + candidates.extend(rl.exits if is_exit else rl.non_exits) + if not len(candidates): + return None + log.debug('Picking a 2nd hop to measure %s from %d choices. is_exit=%s', + relay.nickname, len(candidates), is_exit) + for min_bw_factor in [2, 1.75, 1.5, 1.25, 1]: + min_bw = relay.bandwidth * min_bw_factor + new_candidates = stem_utils.only_relays_with_bandwidth( + cont, candidates, min_bw=min_bw) + if len(new_candidates) > 0: + chosen = rng.choice(new_candidates) + log.debug( + 'Found %d candidate 2nd hops with at least %sx the bandwidth ' + 'of %s. Returning %s (bw=%s).', + len(new_candidates), min_bw_factor, relay.nickname, + chosen.nickname, chosen.bandwidth) + return chosen + candidates = sorted(candidates, key=lambda r: r.bandwidth, reverse=True) + chosen = candidates[0] + log.debug( + 'Didn't find any 2nd hops at least as fast as %s (bw=%s). It's ' + 'probably really fast. Returning %s (bw=%s), the fastest ' + 'candidate we have.', relay.nickname, relay.bandwidth, + chosen.nickname, chosen.bandwidth) + return chosen + + def measure_relay(args, conf, destinations, cb, rl, relay): s = requests_utils.make_session( cb.controller, conf.getfloat('general', 'http_timeout')) @@ -140,24 +174,29 @@ def measure_relay(args, conf, destinations, cb, rl, relay): log.warning('Unable to get destination to measure %s %s', relay.nickname, relay.fingerprint[0:8]) return None - # Pick an exit - exits = rl.exits_can_exit_to(dest.hostname, dest.port) - exits = [e for e in exits if e.fingerprint != relay.fingerprint] - exits = stem_utils.only_relays_with_bandwidth( - cb.controller, exits, min_bw=round(relay.bandwidth*1.25), - max_bw=max(round(relay.bandwidth*2.00), 100)) - if len(exits) < 1: - log.warning('No available exits to help measure %s %s', relay.nickname, - relay.fingerprint[0:8]) + # Pick a relay to help us measure the given relay. If the given relay is an + # exit, then pick a non-exit. Otherwise pick an exit. + helper = None + circ_fps = None + if relay.can_exit_to(dest.hostname, dest.port): + helper = _pick_ideal_second_hop( + relay, dest, rl, cb.controller, is_exit=False) + if helper: + circ_fps = [helper.fingerprint, relay.fingerprint] + else: + helper = _pick_ideal_second_hop( + relay, dest, rl, cb.controller, is_exit=True) + if helper: + circ_fps = [relay.fingerprint, helper.fingerprint] + if not helper: # TODO: Return ResultError of some sort + log.warning('Unable to pick a 2nd hop to help measure %s %s', + relay.nickname, relay.fingerprint[0:8]) return None - exit = rng.choice(exits) + assert helper + assert circ_fps is not None and len(circ_fps) == 2 # Build the circuit - log.debug('We selected exit %s %s (cw=%d) to help measure %s %s (cw=%d)', - exit.nickname, exit.fingerprint[0:8], exit.bandwidth, - relay.nickname, relay.fingerprint[0:8], relay.bandwidth) our_nick = conf['scanner']['nickname'] - circ_fps = [relay.fingerprint, exit.fingerprint] circ_id = cb.build_circuit(circ_fps) if not circ_id: log.warning('Could not build circuit involving %s', relay.nickname) diff --git a/sbws/lib/relaylist.py b/sbws/lib/relaylist.py index 7b3d58a..2cc5fef 100644 --- a/sbws/lib/relaylist.py +++ b/sbws/lib/relaylist.py @@ -88,6 +88,22 @@ class Relay: # it seems that stem parses it as ed25519_master_key return self._from_desc('ed25519_master_key').rstrip('=')
+ def can_exit_to(self, host, port): + if not self.exit_policy: + return False + assert isinstance(host, str) + assert isinstance(port, int) + if not is_valid_ipv4_address(host) and not is_valid_ipv6_address(host): + # It certainly isn't perfect trying to guess if an exit can connect + # to an ipv4/6 address based on the DNS result we got locally. But + # it's the best we can do. + # + # Also, only use the first ipv4/6 we get even if there is more than + # one. + host = resolve(host)[0] + assert is_valid_ipv4_address(host) or is_valid_ipv6_address(host) + return self.exit_policy.can_exit_to(host, port) +
class RelayList: ''' Keeps a list of all relays in the current Tor network and updates it @@ -116,6 +132,10 @@ class RelayList: return self._relays_with_flag(Flag.EXIT)
@property + def non_exits(self): + return self._relays_without_flag(Flag.EXIT) + + @property def guards(self): return self._relays_with_flag(Flag.GUARD)
tor-commits@lists.torproject.org