relay_working_experiment.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/python3
  2. #
  3. import argparse
  4. import shutil
  5. import logging
  6. import random
  7. import os
  8. import multiprocessing
  9. import threading
  10. import time
  11. import json
  12. import gzip
  13. import pickle
  14. import tempfile
  15. #
  16. import stem.control
  17. import stem.descriptor.remote
  18. import stem.process
  19. #
  20. import numa
  21. import log_system_usage
  22. import chutney_manager
  23. import throughput_server
  24. import experiment_client
  25. import experiment
  26. import useful
  27. #
  28. class CustomExperiment(experiment.Experiment):
  29. def __init__(self, use_helgrind, target_tor, *args, **kwargs):
  30. self.use_helgrind = use_helgrind
  31. self.target_tor = target_tor
  32. super().__init__(*args, **kwargs)
  33. #
  34. self.chutney_path = '/home/sengler/code/working/chutney'
  35. self.tor_path = '/home/sengler/code/releases/tor-0.4.2.5'
  36. #
  37. def configure_chutney(self):
  38. #self.nodes = [chutney_manager.Node(tag='a', relay=1, authority=1, torrc='authority.tmpl') for _ in range(self.num_authorities)] + \
  39. # [chutney_manager.Node(tag='r', relay=1, torrc='relay-non-exit.tmpl') for _ in range(self.num_guards)] + \
  40. # [chutney_manager.Node(tag='e', exit=1, torrc='relay.tmpl') for _ in range(self.num_exits)] + \
  41. # [chutney_manager.Node(tag='c', client=1, torrc='client.tmpl') for _ in range(self.num_clients)]
  42. #
  43. #target_tor_path = '/home/sengler/code/working/tor/src/app/tor'
  44. #target_tor_path = '/home/sengler/code/releases/tor-0.4.2.5/src/app/tor'
  45. local_ip = '172.19.156.16'
  46. target_ip = '172.19.156.136'
  47. target_hostname = 'cluck2'
  48. target_optional_args = {}
  49. if self.target_tor is not None:
  50. target_optional_args['tor'] = self.target_tor
  51. if self.use_helgrind:
  52. target_optional_args['valgrind_settings'] = ['--tool=helgrind', '-v', '--suppressions=libevent.supp', '--read-var-info=yes']
  53. #target_optional_args['add_environ_vars'] = {'LD_PRELOAD': '/usr/lib/libprofiler.so.0'}
  54. target_optional_args['add_environ_vars'] = {'LD_PRELOAD': '/usr/lib/libtcmalloc_and_profiler.so.4'}
  55. target_optional_args['ip'] = target_ip
  56. target_optional_args['remote_hostname'] = target_hostname
  57. target_cpu_prof = False #True
  58. target_daemon = False
  59. logs = ['notice']
  60. #if self.use_helgrind:
  61. # valgrind_settings = ['--tool=helgrind', '-v', '--suppressions=libevent.supp', '--read-var-info=yes']
  62. #else:
  63. # valgrind_settings = None
  64. #
  65. self.nodes = [chutney_manager.Node(tag='a', relay=1, authority=1, torrc='authority.tmpl', log_files=logs) for _ in range(self.num_authorities)] + \
  66. [chutney_manager.Node(tag='r', relay=1, torrc='relay-non-exit.tmpl', log_files=logs) for _ in range(self.num_guards)] + \
  67. [chutney_manager.Node(tag='target', relay=1, torrc='relay-non-exit.tmpl',
  68. daemon=target_daemon, log_files=logs, sandbox=0, google_cpu_profiler=target_cpu_prof, **target_optional_args)] + \
  69. [chutney_manager.Node(tag='e', exit=1, torrc='relay.tmpl', log_files=logs) for _ in range(self.num_exits)] + \
  70. [chutney_manager.Node(tag='c', client=1, torrc='client.tmpl', log_files=logs) for _ in range(self.num_clients)]
  71. #
  72. for node in self.nodes:
  73. if not 'num_cpus' in node.options:
  74. node.options['num_cpus'] = 2
  75. #
  76. if not 'ip' in node.options:
  77. node.options['ip'] = local_ip
  78. #
  79. #
  80. numa_remaining = numa.get_numa_overview()
  81. for (node, index) in zip(self.nodes, range(len(self.nodes))):
  82. num_cpus = node.options['num_cpus']
  83. if num_cpus%2 != 0:
  84. num_cpus += 1
  85. #
  86. if node.options['tag'] == 'target':
  87. num_cpus = max(num_cpus, 6)
  88. #
  89. (numa_node, processors) = chutney_manager.numa_scheduler(num_cpus, numa_remaining)
  90. node.options['numa_settings'] = (numa_node, processors)
  91. #
  92. self.proxy_control_ports = [self.nodes[x].guess_control_port(x) for x in range(len(self.nodes)) if ('client', 1) in self.nodes[x].options.items()]
  93. # TODO: ^^ improve this
  94. #
  95. #
  96. def build_circuit_generator(consensus, server_address):
  97. fingerprints = [desc.nickname for desc in consensus]
  98. exit_fingerprints = [desc.nickname for desc in consensus if desc.exit_policy.can_exit_to(*server_address)]
  99. #
  100. target_fingerprint = [desc.nickname for desc in consensus if desc.nickname.endswith('target')][0]
  101. non_exit_fingerprints = list(set(fingerprints)-set(exit_fingerprints)-set([target_fingerprint]))
  102. #
  103. assert len(exit_fingerprints) >= 1, 'Need at least one exit relay'
  104. assert len(non_exit_fingerprints) >= 1, 'Need at least one non-exit relay'
  105. #
  106. #return lambda gen_id=None: [random.choice(non_exit_fingerprints), target_fingerprint, random.choice(exit_fingerprints)]
  107. return lambda gen_id: [non_exit_fingerprints[gen_id%len(non_exit_fingerprints)], target_fingerprint, exit_fingerprints[gen_id%len(exit_fingerprints)]]
  108. '''
  109. fingerprints = [desc.fingerprint for desc in consensus]
  110. exit_fingerprints = [desc.fingerprint for desc in consensus if desc.exit_policy.can_exit_to(*server_address)]
  111. #
  112. target_fingerprint = [desc.fingerprint for desc in consensus if desc.nickname.endswith('target')][0]
  113. non_exit_fingerprints = list(set(fingerprints)-set(exit_fingerprints)-set([target_fingerprint]))
  114. #
  115. assert len(exit_fingerprints) >= 1, 'Need at least one exit relay'
  116. assert len(non_exit_fingerprints) >= 1, 'Need at least one non-exit relay'
  117. #
  118. #return lambda gen_id=None: [random.choice(non_exit_fingerprints), target_fingerprint, random.choice(exit_fingerprints)]
  119. return lambda gen_id: [non_exit_fingerprints[gen_id%len(non_exit_fingerprints)], target_fingerprint, exit_fingerprints[gen_id%len(exit_fingerprints)]]
  120. '''
  121. #
  122. def existing_file(path):
  123. if not os.path.isfile(path):
  124. raise argparse.ArgumentTypeError('The file path is not valid')
  125. return path
  126. #
  127. if __name__ == '__main__':
  128. #
  129. logging.basicConfig(level=logging.DEBUG)
  130. logging.getLogger('stem').setLevel(logging.WARNING)
  131. #
  132. parser = argparse.ArgumentParser(description='Test the network throughput.')
  133. parser.add_argument('num_bytes', type=useful.parse_bytes,
  134. help='number of bytes to send per connection (can also end with \'B\', \'KiB\', \'MiB\', or \'GiB\')', metavar='num-bytes')
  135. parser.add_argument('--buffer-len', type=useful.parse_bytes,
  136. help='size of the send and receive buffers (can also end with \'B\', \'KiB\', \'MiB\', or \'GiB\')', metavar='bytes')
  137. parser.add_argument('--wait-range', type=int, default=0,
  138. help='add a random wait time to each connection so that they don\'t all start at the same time (default is 0)', metavar='time')
  139. parser.add_argument('--target-tor', type=existing_file, default=None,
  140. help='use a different tor binary for the target', metavar='tor-path')
  141. parser.add_argument('--helgrind', action='store_true',
  142. help='log helgrind data')
  143. args = parser.parse_args()
  144. #
  145. #num_clients = 4
  146. #num_guards = 6 # number of relays (including guards)
  147. #num_authorities = 2 # will also act as a relay or guard
  148. #num_exits = 8 # will be used only as an exit
  149. num_clients = 12
  150. num_guards = 14 # number of relays (including guards)
  151. num_authorities = 2 # will also act as a relay or guard
  152. num_exits = 16 # will be used only as an exit
  153. #
  154. experiment_time = time.time()
  155. #
  156. save_data_path = None
  157. measureme_log_path = None
  158. measureme = False
  159. #
  160. start_time = time.time()
  161. #
  162. #num_streams_per_client = 1
  163. num_streams_per_client = 6
  164. logging.info('Starting with {} streams per client'.format(num_streams_per_client))
  165. #
  166. experiment = CustomExperiment(args.helgrind, args.target_tor, save_data_path, measureme_log_path, args.num_bytes,
  167. num_streams_per_client, num_clients, num_guards, num_authorities, num_exits,
  168. build_circuit_generator, args.buffer_len, args.wait_range, measureme, test_network=False)
  169. #
  170. def sleep_then_run(duration, func):
  171. logging.info('Sleeping for {} seconds before running \'{}\''.format(duration, func.__name__))
  172. time.sleep(duration)
  173. logging.info('Done sleeping')
  174. return func()
  175. #
  176. try:
  177. experiment.start_chutney(lambda: experiment.start_throughput_server(lambda: sleep_then_run(20, experiment.start_throughput_clients)))
  178. except KeyboardInterrupt:
  179. logging.info('Stopped (KeyboardInterrupt)')
  180. #
  181. logging.info('Total time: {:.2f} minutes'.format((time.time()-start_time)/60))
  182. #