GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

Commit 20df35c4 authored by Georg Koppen's avatar Georg Koppen

Bug 33009: Require minimum bandwidth for second hop

parent 74ad2d91
......@@ -211,10 +211,16 @@ def _pick_ideal_second_hop(relay, dest, rl, cont, is_exit):
else rl.non_exits
if not len(candidates):
return None
min_relay_bw = rl.exit_min_bw() if is_exit else rl.non_exit_min_bw()
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.consensus_bandwidth * min_bw_factor
# We might have a really slow/new relay. Try to measure it properly by
# using only relays with or above our calculated min_relay_bw (see:
# _calculate_min_bw_second_hop() in relaylist.py).
if min_bw < min_relay_bw:
min_bw = min_relay_bw
new_candidates = stem_utils.only_relays_with_bandwidth(
cont, candidates, min_bw=min_bw)
if len(new_candidates) > 0:
......
......@@ -278,6 +278,9 @@ class RelayList:
[], MAX_RECENT_PRIORITY_RELAY_COUNT, state,
"recent_measurement_attempt"
)
# Start with 0 for the min bw for our second hops
self._exit_min_bw = 0
self._non_exit_min_bw = 0
self._refresh()
def _need_refresh(self):
......@@ -431,6 +434,10 @@ class RelayList:
int(self._measurements_period / 24 / 60 / 60),
self.recent_consensus_count)
# Calculate minimum bandwidth value for 2nd hop after we refreshed
# our available relays.
self._calculate_min_bw_second_hop()
@property
def recent_consensus_count(self):
"""Number of times a new consensus was obtained."""
......@@ -455,3 +462,31 @@ class RelayList:
@property
def recent_measurement_attempt_count(self):
return len(self._recent_measurement_attempt)
def _calculate_min_bw_second_hop(self):
"""
Calculates the minimum bandwidth for both exit and non-exit relays
chosen as a second hop by picking the lowest bandwidth value available
from the top 75% of the respective category.
"""
# Sort our sets of candidates according to bw, lowest amount first.
# It's okay to keep things simple for the calculation and go over all
# exits, including badexits.
exit_candidates = sorted(self.exits,
key=lambda r: r.consensus_bandwidth)
non_exit_candidates = sorted(self.non_exits,
key=lambda r: r.consensus_bandwidth)
# We know the bandwidth is sorted from least to most. Dividing the
# length of the available relays by 4 gives us the position of the
# relay with the lowest bandwidth from the top 75%. We do this both
# for our exit and non-exit candidates.
pos = int(len(exit_candidates)/4)
self._exit_min_bw = exit_candidates[pos].consensus_bandwidth
pos = int(len(non_exit_candidates)/4)
self._non_exit_min_bw = non_exit_candidates[pos].consensus_bandwidth
def exit_min_bw(self):
return self._exit_min_bw
def non_exit_min_bw(self):
return self._non_exit_min_bw
......@@ -18,6 +18,8 @@ def test_init_relays(
Test `init_relays` when creating the RelayList the first time and when a
new consensus is received.
Test that the number of consesus timesamps and relays is correct.
Additionally, make sure the calculated min bw for the second hop for
exit/non-exit relays is correct, too.
"""
state = State(conf['paths']['state_fpath'])
# There is no need to mock datetime to update the consensus, since the
......@@ -31,6 +33,9 @@ def test_init_relays(
# The actual number of relays in the consensus
assert len(relay_list._relays) == 6433
fps = {r.fingerprint for r in relay_list._relays}
# The calculated min bw for the second hop
assert 2100000 == relay_list._exit_min_bw
assert 220000 == relay_list._non_exit_min_bw
# One hour later there is a new consensus
relay_list._controller = controller_1h_later
......@@ -44,6 +49,9 @@ def test_init_relays(
fps_1h_later = {r.fingerprint for r in relay_list._relays}
added_fps = fps_1h_later.difference(fps)
assert 6505 == 6433 + len(added_fps)
# The calculated min bw for the second hop
assert 2120000 == relay_list._exit_min_bw
assert 200000 == relay_list._non_exit_min_bw
# Five days later plus 1 second.
# The first consensus timestamp will get removed.
......@@ -62,6 +70,9 @@ def test_init_relays(
# The number of relays will be the number of relays in the cosensus plus
# the added ones minus the removed ones.
assert 6925 == 6505 + len(added_fps) - len(removed_fps)
# The calculated min bw for the second hop
assert 2790000 == relay_list._exit_min_bw
assert 110000 == relay_list._non_exit_min_bw
def test_increment_recent_measurement_attempt(args, conf, controller):
......
Markdown is supported
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