bytecounts.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #!/usr/bin/env python3
  2. import random # For simulation, not cryptography!
  3. import math
  4. import sys
  5. import os
  6. import logging
  7. import resource
  8. import sympy
  9. sys.path.append("..")
  10. import network
  11. import dirauth
  12. import relay
  13. import client
  14. class BandwidthMeasurer:
  15. def __init__(self, numdirauths, numrelays, numclients):
  16. # Start some dirauths
  17. self.dirauthaddrs = []
  18. self.dirauths = []
  19. for i in range(numdirauths):
  20. dira = dirauth.DirAuth(i, numdirauths)
  21. self.dirauths.append(dira)
  22. self.dirauthaddrs.append(dira.netaddr)
  23. # Start some relays
  24. self.relays = []
  25. for i in range(numrelays):
  26. self.startrelay()
  27. # The fallback relays are a hardcoded list of a small fraction
  28. # of the relays, used by clients for bootstrapping
  29. numfallbackrelays = 1
  30. fallbackrelays = self.relays[0:1]
  31. for r in fallbackrelays:
  32. r.set_is_fallbackrelay()
  33. network.thenetwork.setfallbackrelays(fallbackrelays)
  34. # Tick the epoch to build the first consensus
  35. network.thenetwork.nextepoch()
  36. # Start some clients
  37. self.clients = []
  38. for i in range(numclients):
  39. self.startclient()
  40. # Throw away all the performance statistics to this point
  41. for d in self.dirauths: d.perfstats.reset()
  42. for r in self.relays: r.perfstats.reset()
  43. # The clients' stats are already at 0, but they have the
  44. # "bootstrapping" flag set, which we want to keep, so we
  45. # won't reset them.
  46. self.allcircs = []
  47. # Tick the epoch to bootstrap the clients
  48. network.thenetwork.nextepoch()
  49. def startrelay(self):
  50. # Relay bandwidths (at least the ones fast enough to get used)
  51. # in the live Tor network (as of Dec 2019) are well approximated
  52. # by (200000-(200000-25000)/3*log10(x)) where x is a
  53. # uniform integer in [1,2500]
  54. x = random.randint(1,2500)
  55. bw = int(200000-(200000-25000)/3*math.log10(x))
  56. self.relays.append(relay.Relay(self.dirauthaddrs, bw, 0))
  57. def stoprelay(self):
  58. self.relays[1].terminate()
  59. del self.relays[1]
  60. def startclient(self):
  61. self.clients.append(client.Client(self.dirauthaddrs))
  62. def stopclient(self):
  63. self.clients[0].terminate()
  64. del self.clients[0]
  65. def buildcircuit(self):
  66. bwm.allcircs.append(bwm.clients[0].channelmgr.new_circuit())
  67. def getstats(self):
  68. # gather stats
  69. totsent = 0
  70. totrecv = 0
  71. totbytes = 0
  72. dirasent = 0
  73. dirarecv = 0
  74. dirabytes = 0
  75. relaysent = 0
  76. relayrecv = 0
  77. relaybytes = 0
  78. clisent = 0
  79. clirecv = 0
  80. clibytes = 0
  81. for d in self.dirauths:
  82. logging.debug("%s", d.perfstats)
  83. dirasent += d.perfstats.bytes_sent
  84. dirarecv += d.perfstats.bytes_received
  85. dirabytes += d.perfstats.bytes_sent + d.perfstats.bytes_received
  86. totsent += dirasent
  87. totrecv += dirarecv
  88. totbytes += dirabytes
  89. for r in self.relays:
  90. logging.debug("%s", r.perfstats)
  91. relaysent += r.perfstats.bytes_sent
  92. relayrecv += r.perfstats.bytes_received
  93. relaybytes += r.perfstats.bytes_sent + r.perfstats.bytes_received
  94. totsent += relaysent
  95. totrecv += relayrecv
  96. totbytes += relaybytes
  97. for c in self.clients:
  98. logging.debug("%s", c.perfstats)
  99. clisent += c.perfstats.bytes_sent
  100. clirecv += c.perfstats.bytes_received
  101. clibytes += c.perfstats.bytes_sent + c.perfstats.bytes_received
  102. totsent += clisent
  103. totrecv += clirecv
  104. totbytes += clibytes
  105. logging.info("DirAuths sent=%s recv=%s bytes=%s" % \
  106. (dirasent, dirarecv, dirabytes))
  107. logging.info("Relays sent=%s recv=%s bytes=%s" % \
  108. (relaysent, relayrecv, relaybytes))
  109. logging.info("Client sent=%s recv=%s bytes=%s" % \
  110. (clisent, clirecv, clibytes))
  111. logging.info("Total sent=%s recv=%s bytes=%s" % \
  112. (totsent, totrecv, totbytes))
  113. # Reset bootstrap flag
  114. for d in self.dirauths: d.perfstats.is_bootstrapping = False
  115. for r in self.relays: r.perfstats.is_bootstrapping = False
  116. for c in self.clients: c.perfstats.is_bootstrapping = False
  117. return (dirabytes, relaybytes, clibytes)
  118. def endepoch(self):
  119. # Close circuits
  120. for c in self.allcircs:
  121. c.close()
  122. self.allcircs = []
  123. # Reset stats
  124. for d in self.dirauths: d.perfstats.reset()
  125. for r in self.relays: r.perfstats.reset()
  126. for c in self.clients: c.perfstats.reset()
  127. network.thenetwork.nextepoch()
  128. if __name__ == '__main__':
  129. # Args: womode snipauthmode numrelays randseed
  130. if len(sys.argv) != 5:
  131. sys.stderr.write("Usage: womode snipauthmode numrelays randseed\n")
  132. sys.exit(1)
  133. womode = network.WOMode[sys.argv[1].upper()]
  134. snipauthmode = network.SNIPAuthMode[sys.argv[2].upper()]
  135. numrelays = int(sys.argv[3])
  136. randseed = int(sys.argv[4])
  137. # Use symbolic byte counter mode
  138. network.symbolic_byte_counters = True
  139. # Seed the PRNG. On Ubuntu 18.04, this in fact makes future calls
  140. # to (non-cryptographic) random numbers deterministic. On Ubuntu
  141. # 16.04, it does not.
  142. random.seed(randseed)
  143. loglevel = logging.INFO
  144. # Uncomment to see all the debug messages
  145. # loglevel = logging.DEBUG
  146. logging.basicConfig(level=loglevel,
  147. format="%(asctime)s:%(levelname)s:%(message)s")
  148. logging.info("Starting simulation")
  149. # Set the Walking Onions style to use
  150. network.thenetwork.set_wo_style(womode, snipauthmode)
  151. bwm = BandwidthMeasurer(9, numrelays, 0)
  152. stats = dict()
  153. logging.info("R_N = %d, R_B = 0, C_N = 0, C_B = 0, circs = 0", numrelays)
  154. stats[(numrelays, 0, 0, 0, 0)] = bwm.getstats()
  155. # Bootstrap one relay
  156. bwm.startrelay()
  157. bwm.endepoch()
  158. logging.info("R_N = %d, R_B = 1, C_N = 0, C_B = 0, circs = 0", numrelays)
  159. stats[(numrelays, 1, 0, 0, 0)] = bwm.getstats()
  160. # Bootstrap one client
  161. bwm.stoprelay()
  162. bwm.startclient()
  163. bwm.endepoch()
  164. logging.info("R_N = %d, R_B = 0, C_N = 0, C_B = 1, circs = 0", numrelays)
  165. stats[(numrelays, 0, 0, 1, 0)] = bwm.getstats()
  166. # No changes, so the client is now not bootstrapping
  167. bwm.endepoch()
  168. logging.info("R_N = %d, R_B = 0, C_N = 1, C_B = 0, circs = 0", numrelays)
  169. stats[(numrelays, 0, 1, 0, 0)] = bwm.getstats()
  170. # No more bootstrapping, but build one circuit
  171. bwm.buildcircuit()
  172. logging.info("R_N = %d, R_B = 0, C_N = 1, C_B = 0, circs = 1", numrelays)
  173. stats[(numrelays, 0, 1, 0, 1)] = bwm.getstats()
  174. bwm.endepoch()
  175. # No more bootstrapping, but build two circuits
  176. bwm.buildcircuit()
  177. bwm.buildcircuit()
  178. logging.info("R_N = %d, R_B = 0, C_N = 1, C_B = 0, circs = 2", numrelays)
  179. stats[(numrelays, 0, 1, 0, 2)] = bwm.getstats()
  180. bwm.endepoch()
  181. print("\n")
  182. print('Total relay bytes:')
  183. print(' R_N * (', stats[(numrelays, 0, 0, 0, 0)][1]/numrelays, ')')
  184. print('+ R_B * (', stats[(numrelays, 1, 0, 0, 0)][1] - stats[(numrelays, 0, 0, 0, 0)][1], ')')
  185. print('+ C_N * (', stats[(numrelays, 0, 1, 0, 0)][1] - stats[(numrelays, 0, 0, 0, 0)][1], ')')
  186. print('+ C_B * (', stats[(numrelays, 0, 0, 1, 0)][1] - stats[(numrelays, 0, 0, 0, 0)][1], ')')
  187. print('+ circ * (', stats[(numrelays, 0, 1, 0, 1)][1] - stats[(numrelays, 0, 1, 0, 0)][1], ')')
  188. print(' check ', stats[(numrelays, 0, 1, 0, 2)][1] - stats[(numrelays, 0, 1, 0, 1)][1])
  189. print("\n")
  190. print('Total client bytes:')
  191. print(' R_N * (', stats[(numrelays, 0, 0, 0, 0)][2]/numrelays, ')')
  192. print('+ R_B * (', stats[(numrelays, 1, 0, 0, 0)][2] - stats[(numrelays, 0, 0, 0, 0)][2], ')')
  193. print('+ C_N * (', stats[(numrelays, 0, 1, 0, 0)][2] - stats[(numrelays, 0, 0, 0, 0)][2], ')')
  194. print('+ C_B * (', stats[(numrelays, 0, 0, 1, 0)][2] - stats[(numrelays, 0, 0, 0, 0)][2], ')')
  195. print('+ circ * (', stats[(numrelays, 0, 1, 0, 1)][2] - stats[(numrelays, 0, 1, 0, 0)][2], ')')
  196. print(' check ', stats[(numrelays, 0, 1, 0, 2)][2] - stats[(numrelays, 0, 1, 0, 1)][2])