Commit 430fe9be authored by Matt Traudt's avatar Matt Traudt
Browse files

Rename client to scanner

GH: closes #78
parent 15a38bb3
......@@ -2,5 +2,5 @@
datadir = ${sbws_home}/datadir
sbws_home = /home/user/.sbws
[client]
[scanner]
nickname = IDidntEditTheSBWSConfig
......@@ -9,7 +9,7 @@ What is a bandwidth authority?
.. todo:: see :ref:`XX`
A server that runs ``sbws client``, the bandwidth scanner that performs the
A server that runs ``sbws scanner``, the bandwidth scanner that performs the
measurements. It can run on the same machine as a directory authority or
somehow send its results to it.
......@@ -30,19 +30,19 @@ What is a helper relay?
.. todo:: see :ref:`XX`
Is it the sbws client or server that gives the v3bw files to the directory authority?
Is it the sbws scanner or server that gives the v3bw files to the directory authority?
-------------------------------------------------------------------------------------
Technically, neither.
In the suggested setup, the machine running ``sbws client`` continuously will
In the suggested setup, the machine running ``sbws scanner`` continuously will
also periodically run ``sbws generate`` to produce a v3bw file for the
directory authority to read.
.. todo:: see :ref:`XX`
Is it the sbws client or server that I need to run close to a fast relay?
Is it the sbws scanner or server that I need to run close to a fast relay?
-------------------------------------------------------------------------
The sbws server.
......@@ -52,14 +52,14 @@ The sbws server.
Why doesn't sbws just use a web/file server instead of custom software?
-----------------------------------------------------------------------
To lower protocol overhead and to allow sbws clients to request a wide range of
To lower protocol overhead and to allow sbws scanners to request a wide range of
bytes.
Sbws essentially has no overhead, with only about 70 bytes used in a handshake
at the beginning of each connection. A connection can be used to perform
multiple measurements of one relay.
At the time of writing, sbws clients are allowed to request from the server
At the time of writing, sbws scanners are allowed to request from the server
between 1 byte and 1,073,741,824 bytes (1 GiB). That's a lot of possibilities
and a ton of storage space.
......
......@@ -4,10 +4,10 @@ sbws.core package
Submodules
----------
sbws.core.client module
sbws.core.scanner module
---------------------------
.. automodule:: sbws.core.client
.. automodule:: sbws.core.scanner
:members:
:undoc-members:
:show-inheritance:
......
......@@ -11,7 +11,7 @@ Simple Bandwidth Scanner Specification
Some of the Tor directory authorities runs bandwidth scanners to measure the
bandwidth of relays and include their measurements in their network status
votes. Client use the consensus of these weights to inform their path
votes. Scanner use the consensus of these weights to inform their path
selection process with the hope that every circuit they build will have roughly
equal performance, regardless of the relays chosen. This achieves a form of
load balancing.
......@@ -30,7 +30,7 @@ Rome 2018 Tor Project meeting. This document describes the implementation
contained within the accompanying ``sbws`` package.
First we cover the parts of sbws that continuously perform measurements;
namely, the client that builds 2 hop Tor circuits through a target and helper
namely, the scanner that builds 2 hop Tor circuits through a target and helper
relay to a waiting server. Next we describe the process of periodically turning
recently gathered results into an aggregate format ready for including in a
bandwidth authority's votes.
......@@ -40,16 +40,16 @@ bandwidth authority's votes.
--------------------------------------
First and foremost, the Tor network needs one or more helper relays to act as
exits in the two hop circuits that sbws measurement clients build. These helper
exits in the two hop circuits that sbws measurement scanners build. These helper
relays need not be proper exits, but merely must support exiting to a single IP
address and port, at which is listening an sbws server. Ideally the helper
relay and sbws server are running on the same physical hardware. The sbws
servers listen for clients to authenticate to them, and once successfully
authenticated, wait for clients to request arbitrary numbers of bytes to be
servers listen for scanners to authenticate to them, and once successfully
authenticated, wait for scanners to request arbitrary numbers of bytes to be
sent to them.
Every directory authority that wishes to also vote on relay bandwidth must then
run one or more sbws clients. The clients run continuously, constantly building
run one or more sbws scanners. The scanners run continuously, constantly building
circuits and measuring the amount of bandwidth each relay is capable of
handling on the measurement circuit. Over these circuits it collects RTT data
(by repeatedly requesting a single byte from the server) and available
......@@ -91,10 +91,10 @@ addresses on the local machine. Finally we have a simple exit policy that
allows exiting to the local machine on a single port and rejects all other exit
traffic. *The relay will not get the exit flag.*
2.2 Configuring the sbws client
2.2 Configuring the sbws scanner
-------------------------------
For an sbws client, its Tor client configuration is even simpler. In addition
For an sbws scanner, its Tor scanner configuration is even simpler. In addition
to making sure it has a SocksPort, ControlPort, and some form of ControlPort
authentication enabled, it is recommended circuit build timeout options be set
as such.
......@@ -104,7 +104,7 @@ as such.
LearnCircuitBuildTimeout 0
CircuitBuildTimeout 10
When the sbws client starts up and connects to Tor, it will set the following
When the sbws scanner starts up and connects to Tor, it will set the following
two options.
::
......@@ -113,20 +113,20 @@ two options.
__LeaveStreamsUnattached 1
The former simply to cut down on the number of unused circuits and the latter
so that the client can attach streams to circuits manually.
so that the scanner can attach streams to circuits manually.
2.3 Sbws client/server authentication
2.3 Sbws scanner/server authentication
-------------------------------------
**XXX This will be changed very soon to be more user friendly, but the idea is
the same.**
The sbws client keeps a ``passwords.txt`` file containing a single non-comment
The sbws scanner keeps a ``passwords.txt`` file containing a single non-comment
line containing a 64 character password consisting only of characters in the
space ``a-zA-Z0-9``.
The sbws similarly keeps a ``passwords.txt``, but its contains many 64
character passwords. When a client connects, it must provide one of the 64
character passwords. When a scanner connects, it must provide one of the 64
character passwords in the server's ``passwords.txt``.
3. How it all works
......@@ -186,51 +186,51 @@ sooner.
3.2 Simple wire protocol
------------------------
In this subsection, the client/server communication that takes place after a
In this subsection, the scanner/server communication that takes place after a
Tor circuit is built and a TCP connection created in it is described.
3.2.1 Simple handshake
----------------------
After initiating a TCP connection over Tor to the server, the sbws client sends
After initiating a TCP connection over Tor to the server, the sbws scanner sends
4 magic bytes indicating it intends to speak sbws' protocol. If the first four
bytes an sbws server receives are not the correct magic bytes, the server
SHOULD close the connection.
If the client sends the correct magic bytes, the server does nothing in
response. Therefore, the client SHOULD immediately followup with the version of
If the scanner sends the correct magic bytes, the server does nothing in
response. Therefore, the scanner SHOULD immediately followup with the version of
the wire protocol it will speak. This version is an integer, but is sent as a
string followed by a newline. So version 1 would be sent as the two byte
string, ``"1\n"``.
If the server does not support the version that the client sent, it MUST
If the server does not support the version that the scanner sent, it MUST
immediately close the connection. Otherwise, the server does nothing in
response. Therefore, the client SHOULD immediately followup with its 64
response. Therefore, the scanner SHOULD immediately followup with its 64
character password.
Upon receiving the client's full password, the server checks if it is valid. If
Upon receiving the scanner's full password, the server checks if it is valid. If
it is invalid, the server MUST immediately close the connection. Otherwise, the server
MUST send to the client the 1 byte success code.
MUST send to the scanner the 1 byte success code.
Once the client receives the success code, the handshake is complete and the
Once the scanner receives the success code, the handshake is complete and the
simple loop may begin.
3.2.2 Simple loop
-----------------
To begin the loop, the sbws client decides how many bytes it would like to
To begin the loop, the sbws scanner decides how many bytes it would like to
download from the server. To inform the server, it encodes an integer as text
followed by a newline character. For example, to request 123 bytes, the client would
followed by a newline character. For example, to request 123 bytes, the scanner would
send to the server the string of four bytes ``"123\n"``.
After indicating success to the client in the simple handshake, the server
begins listening for the client to send a line as described above. Once the
After indicating success to the scanner in the simple handshake, the server
begins listening for the scanner to send a line as described above. Once the
server reads a newline character (``'\n'``), it parses the string into an
integer and proceeds to send the client that many bytes as fast as possible.
integer and proceeds to send the scanner that many bytes as fast as possible.
Immediately after requesting some amount of bytes from the server, the client
Immediately after requesting some amount of bytes from the server, the scanner
begins listening for the server to respond with arbitrary bytes until it has
sent the amount it was expecting. At this point the client MUST close the
sent the amount it was expecting. At this point the scanner MUST close the
connection if it does not wish to make any more requests. Otherwise, the simple
loop starts over.
......@@ -245,10 +245,10 @@ stored -- **one per line** -- in text files in a data directory.
The text files are simply named after the date. For example:
``2018-03-20.txt``.
The sbws client only appends to these files, and it automatically starts a new
The sbws scanner only appends to these files, and it automatically starts a new
file when the system's clock ticks past midnight.
To avoid any weird timezone-related issues, consumers of sbws client data (such
To avoid any weird timezone-related issues, consumers of sbws scanner data (such
as the generate and stats scripts) should read more files than strictly
necessary. For example, if the validity period is 5 days, they should read 6
days of files. Because all results have a Unix timestamp, consumers of sbws
......
......@@ -6,7 +6,7 @@
# Days into the past that measurements are considered valid
data_period = 5
[client]
[scanner]
# A human-readable string with chars in a-zA-Z0-9 to identify your scanner
nickname = IDidntEditTheSBWSConfig
# Maximum number of bytes to read for each sock.recv() call
......
[paths]
datadir = ${sbws_home}/datadir
[client]
[scanner]
nickname = IDidntEditTheSBWSConfig
......@@ -113,7 +113,7 @@ def main(args, conf):
fresh_days, datadir, success_only=True)
if len(results) < 1:
log.warning('No recent results, so not generating anything. (Have you '
'ran sbws client recently?)')
'ran sbws scanner recently?)')
return
data = group_results_by_relay(results)
data_lines = [result_data_to_v3bw_line(data, fp) for fp in data]
......
......@@ -6,7 +6,7 @@ ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
def gen_parser(sub):
d = 'Generate a password suitable for use by a sbws client for '\
d = 'Generate a password suitable for use by a sbws scanner for '\
'authenticating to an sbws server.'
p = sub.add_parser('pwgen', formatter_class=ArgumentDefaultsHelpFormatter,
description=d)
......
......@@ -81,7 +81,7 @@ def timed_recv_from_server(sock, conf, yet_to_read):
assert yet_to_read > 0
start_time = time_now()
while yet_to_read > 0:
limit = min(conf.getint('client', 'max_recv_per_read'), yet_to_read)
limit = min(conf.getint('scanner', 'max_recv_per_read'), yet_to_read)
try:
read_this_time = len(sock.recv(limit))
except (socket.timeout, ConnectionResetError, BrokenPipeError) as e:
......@@ -99,7 +99,7 @@ def measure_rtt_to_server(sock, conf):
not all of them can be made, return None. Otherwise return a list of the
RTTs (in seconds). '''
rtts = []
for _ in range(0, conf.getint('client', 'num_rtts')):
for _ in range(0, conf.getint('scanner', 'num_rtts')):
start_time = time_now()
if not tell_server_amount(sock, MIN_REQ_BYTES):
log.info('Unable to ping server on %d', sock.fileno())
......@@ -148,7 +148,7 @@ def measure_relay(args, conf, helpers, cb, rl, relay):
4.4. write down the results
'''
our_nick = conf['client']['nickname']
our_nick = conf['scanner']['nickname']
helper = helpers.next(blacklist=[relay.fingerprint])
if not helper:
log.warning('Unable to get helper to measure %s', relay.nickname)
......@@ -169,8 +169,8 @@ def measure_relay(args, conf, helpers, cb, rl, relay):
# circuit when we connect()
stem_utils.add_event_listener(
cb.controller, listener, EventType.STREAM)
s = make_socket(conf['client']['tor_socks_host'],
conf.getint('client', 'tor_socks_port'))
s = make_socket(conf['scanner']['tor_socks_host'],
conf.getint('scanner', 'tor_socks_port'))
# This call blocks until we are connected (or give up). We get attched
# to the right circuit in the background.
connected = socket_connect(s, helper.server_host, helper.server_port)
......@@ -197,13 +197,13 @@ def measure_relay(args, conf, helpers, cb, rl, relay):
# SECOND: measure throughput on this circuit. Start with what should be a
# small amount
results = []
expected_amount = conf.getint('client', 'initial_read_request')
num_downloads = conf.getint('client', 'num_downloads')
expected_amount = conf.getint('scanner', 'initial_read_request')
num_downloads = conf.getint('scanner', 'num_downloads')
download_times = {
'toofast': conf.getfloat('client', 'download_toofast'),
'min': conf.getfloat('client', 'download_min'),
'target': conf.getfloat('client', 'download_target'),
'max': conf.getfloat('client', 'download_max'),
'toofast': conf.getfloat('scanner', 'download_toofast'),
'min': conf.getfloat('scanner', 'download_min'),
'target': conf.getfloat('scanner', 'download_target'),
'max': conf.getfloat('scanner', 'download_max'),
}
while len(results) < num_downloads:
if expected_amount == MAX_REQ_BYTES:
......@@ -314,7 +314,7 @@ def run_speedtest(args, conf):
args, conf, controller=controller)
if not helpers:
fail_hard(error_msg)
max_pending_results = conf.getint('client', 'measurement_threads')
max_pending_results = conf.getint('scanner', 'measurement_threads')
pool = Pool(max_pending_results)
pending_results = []
while True:
......@@ -332,11 +332,11 @@ def run_speedtest(args, conf):
def gen_parser(sub):
d = 'The client side of sbws. This should be run on a well-connected '\
d = 'The scanner side of sbws. This should be run on a well-connected '\
'machine on the Internet with a healthy amount of spare bandwidth. '\
'This continuously builds circuits, measures relays, and dumps '\
'results into a datadir, commonly found in ~/.sbws'
sub.add_parser('client', formatter_class=ArgumentDefaultsHelpFormatter,
sub.add_parser('scanner', formatter_class=ArgumentDefaultsHelpFormatter,
description=d)
......@@ -344,7 +344,7 @@ def main(args, conf):
if not is_initted(args.directory):
fail_hard('Sbws isn\'t initialized. Try sbws init')
if conf.getint('client', 'measurement_threads') < 1:
if conf.getint('scanner', 'measurement_threads') < 1:
fail_hard('Number of measurement threads must be larger than 1')
if conf['tor']['control_type'] not in ['port', 'socket']:
......
from ..util.simpleauth import authenticate_client
from ..util.simpleauth import authenticate_scanner
from ..util.sockio import read_line
from sbws.globals import (fail_hard, is_initted)
from sbws.globals import (MIN_REQ_BYTES, MAX_REQ_BYTES, SOCKET_TIMEOUT)
......@@ -16,8 +16,8 @@ log = logging.getLogger(__name__)
def gen_parser(sub):
d = 'The server side of sbws. This should be run on the same machine as '\
'a helper relay. This listens for clients connections and responds '\
'with the number of bytes the client requests.'
'a helper relay. This listens for scanners connections and responds '\
'with the number of bytes the scanner requests.'
sub.add_parser('server', formatter_class=ArgumentDefaultsHelpFormatter,
description=d)
......@@ -88,9 +88,9 @@ _generate_random_string.alphabet = list('abcdefghijklmnopqrstuvwxyz'
# _generate_random_string.last_log = time_now()
def write_to_client(sock, conf, amount):
def write_to_scanner(sock, conf, amount):
''' Returns True if successful; else False '''
log.debug('Sending client no. %d %d bytes', sock.fileno(), amount)
log.debug('Sending scanner no. %d %d bytes', sock.fileno(), amount)
while amount > 0:
amount_this_time = min(conf.getint('server', 'max_send_per_write'),
amount)
......@@ -106,13 +106,13 @@ def write_to_client(sock, conf, amount):
def new_thread(args, conf, sock):
def closure():
client_name = authenticate_client(
scanner_name = authenticate_scanner(
sock, conf['server.passwords'])
if not client_name:
log.info('Client did not provide valid auth')
if not scanner_name:
log.info('Scanner did not provide valid auth')
close_socket(sock)
return
log.info('%s authenticated on %d', client_name, sock.fileno())
log.info('%s authenticated on %d', scanner_name, sock.fileno())
while True:
send_amount = get_send_amount(sock)
if send_amount is None:
......@@ -121,10 +121,10 @@ def new_thread(args, conf, sock):
break
if send_amount < MIN_REQ_BYTES or send_amount > MAX_REQ_BYTES:
log.warning('%s requested %d bytes, which is not valid',
client_name, send_amount)
scanner_name, send_amount)
break
write_to_client(sock, conf, send_amount)
log.info('%s on %d went away', client_name, sock.fileno())
write_to_scanner(sock, conf, send_amount)
log.info('%s on %d went away', scanner_name, sock.fileno())
close_socket(sock)
thread = Thread(target=closure)
return thread
......
......@@ -7,11 +7,11 @@ log = logging.getLogger(__name__)
G_PKG_DIR = os.path.abspath(os.path.dirname(__file__))
# Minimum and maximum number of bytes a client is allowed to request from a
# Minimum and maximum number of bytes a scanner is allowed to request from a
# server. If these are changed, a WIRE_PROTO_VER bump is required, which also
# happens to require an sbws major version bump.
#
# Note for smart people and people who pull out Wireshark: Even if the client
# Note for smart people and people who pull out Wireshark: Even if the scanner
# requests 1 byte, that request and the 1 byte response will each be carried
# over the Internet in 514 byte Tor cells. Theoretically we could bump the
# minimum request size up to ~498 bytes, but I see no reason why we should.
......
......@@ -36,7 +36,7 @@ class RelayPrioritizer:
self.relay_list = relay_list
self.result_dump = result_dump
self.measure_authorities = conf.getboolean(
'client', 'measure_authorities')
'scanner', 'measure_authorities')
def best_priority(self):
''' Return a generator containing the best priority relays.
......
......@@ -93,7 +93,7 @@ def load_recent_results_in_datadir(fresh_days, datadir, success_only=False):
results = trim_results(fresh_days, results)
if len(results) == 0:
log.warning('Results files that are valid not found. '
'Probably sbws client was not run first or '
'Probably sbws scanner was not run first or '
'it ran more than %d days ago or '
'it was using a different datadir than %s.', data_period,
datadir)
......@@ -137,12 +137,12 @@ class Result:
self.nickname = nickname
self.address = address
def __init__(self, relay, circ, server_host, client_nick, t=None):
def __init__(self, relay, circ, server_host, scanner_nick, t=None):
self._relay = Result.Relay(relay.fingerprint, relay.nickname,
relay.address)
self._circ = circ
self._server_host = server_host
self._scanner = client_nick
self._scanner = scanner_nick
self._time = time_now() if t is None else t
@property
......
import sbws.core.cleanup
import sbws.core.client
import sbws.core.scanner
import sbws.core.generate
import sbws.core.init
import sbws.core.pwgen
......@@ -36,8 +36,8 @@ def main():
known_commands = {
'cleanup': {'f': sbws.core.cleanup.main,
'a': def_args, 'kw': def_kwargs},
'client': {'f': sbws.core.client.main,
'a': def_args, 'kw': def_kwargs},
'scanner': {'f': sbws.core.scanner.main,
'a': def_args, 'kw': def_kwargs},
'generate': {'f': sbws.core.generate.main,
'a': def_args, 'kw': def_kwargs},
'init': {'f': sbws.core.init.main,
......
......@@ -97,7 +97,7 @@ def validate_config(conf):
errors = []
errors.extend(_validate_general(conf))
errors.extend(_validate_cleanup(conf))
errors.extend(_validate_client(conf))
errors.extend(_validate_scanner(conf))
errors.extend(_validate_server(conf))
errors.extend(_validate_server_passwords(conf))
errors.extend(_validate_tor(conf))
......@@ -143,9 +143,9 @@ def _validate_paths(conf):
return errors
def _validate_client(conf):
def _validate_scanner(conf):
errors = []
sec = 'client'
sec = 'scanner'
err_tmpl = Template('$sec/$key ($val): $e')
ints = {
'max_recv_per_read': {'minimum': 1, 'maximum': None},
......
import sbws.core.cleanup
import sbws.core.client
import sbws.core.scanner
import sbws.core.generate
import sbws.core.init
import sbws.core.pwgen
......@@ -28,7 +28,7 @@ def create_parser():
help='Name of the .sbws directory')
sub = p.add_subparsers(dest='command')
sbws.core.cleanup.gen_parser(sub)
sbws.core.client.gen_parser(sub)
sbws.core.scanner.gen_parser(sub)
sbws.core.generate.gen_parser(sub)
sbws.core.init.gen_parser(sub)
sbws.core.pwgen.gen_parser(sub)
......
......@@ -10,18 +10,18 @@ PW_LEN = 64
log = logging.getLogger(__name__)
def authenticate_client(sock, conf_section):
''' Use this on the server side to read bytes from the client and properly
authenticate them. Return the name of the client who has authenticated if
def authenticate_scanner(sock, conf_section):
''' Use this on the server side to read bytes from the scanner and properly
authenticate them. Return the name of the scanner who has authenticated if
they provided a good password, otherwise None.
:param socket.socket sock: The open and blocking socket to use to
communicate with the client
communicate with the scanner
:param configparser.SectionProxy conf_section: The ``[server.passwords]``
section from the sbws config file
:returns: The name of the client that successfully authenticated as a str,
:returns: The name of the scanner that successfully authenticated as a str,
as pulled from the ``[server.passwords]`` section of the config. If
the client couldn't authenticate, returns None
the scanner couldn't authenticate, returns None
'''
assert sock.fileno() > 0
assert len(conf_section) > 0
......@@ -36,7 +36,7 @@ def authenticate_client(sock, conf_section):
line = read_line(sock, max_len=4)
if line != str(wire_proto_ver):
log.warning('Client gave protocol version %s but we support %d', line,
log.warning('Scanner gave protocol version %s but we support %d', line,
wire_proto_ver)
return None
......@@ -49,8 +49,8 @@ def authenticate_client(sock, conf_section):
log.warning(e)
return None
client_name = _is_valid_password(pw, conf_section)
if not client_name:
scanner_name = _is_valid_password(pw, conf_section)
if not scanner_name:
log.warning('Invalid password')
return None
......@@ -59,7 +59,7 @@ def authenticate_client(sock, conf_section):
except (socket.timeout, ConnectionResetError, BrokenPipeError) as e:
log.warning(e)
return None
return client_name
return scanner_name
def authenticate_to_server(sock, pw):
......@@ -93,7 +93,7 @@ def authenticate_to_server(sock, pw):
def _is_valid_password(pw, conf_section):
''' Returns the key in the [server.passwords] section of the config for the
password the client provided (AKA: if the client provided a valid
password the scanner provided (AKA: if the scanner provided a valid
password). Otherwise return None '''
assert len(conf_section) > 0
if len(pw) != PW_LEN:
......
......@@ -68,11 +68,11 @@ def dotsbws_error_result(empty_dotsbws_datadir):
nick = 'CowSayWhat'
relay_ip = '169.254.100.1'
server_ip = '169.254.100.2'
client_nick = 'SBWSclient'
scanner_nick = 'SBWSscanner'
msg = 'UnitTest error message'
t = time.time()
relay = Result.Relay(fp1, nick, relay_ip)
result = ResultError(relay, circ, server_ip, client_nick, t=t, msg=msg)
result = ResultError(relay, circ, server_ip, scanner_nick, t=t, msg=msg)
args = _PseudoArguments(directory=empty_dotsbws_datadir.name)
conf = get_config(args)
dd = conf['paths']['datadir']
......@@ -91,13 +91,13 @@ def dotsbws_success_result(empty_dotsbws_datadir):
nick = 'CowSayWhat'
relay_ip = '169.254.100.1'
server_ip = '169.254.100.2'
client_nick = 'SBWSclient'
scanner_nick = 'SBWSscanner'
rtts = [4.242]
downloads = [{'duration': 4, 'amount': 40}]
t = time.time()
relay = Result.Relay(fp1, nick, relay_ip)
result = ResultSuccess(rtts, downloads, relay, circ, server_ip,
client_nick, t=t)
scanner_nick, t=t)
args = _PseudoArguments(directory=empty_dotsbws_datadir.name)
conf = get_config(args)
dd = conf['paths']['datadir']
......@@ -119,20 +119,20 @@ def dotsbws_success_result_one_relay(empty_dotsbws_datadir):
nick = 'CowSayWhat'
relay_ip = '169.254.100.1'
server_ip = '169.254.100.2'
client_nick = 'SBWSclient'
scanner_nick = 'SBWSscanner'
rtts = [5, 25]
downloads = [{'duration': 4, 'amount': 40}]
t = time.time()
relay = Result.Relay(fp1, nick, relay_ip)
result = ResultSuccess(rtts, downloads, relay, circ, server_ip,