[or-cvs] r11650: Introducing 'simulation'-mode: This can be used for generati (in torflow/trunk: . TorCtl)
renner at seul.org
renner at seul.org
Wed Sep 26 09:46:45 UTC 2007
Author: renner
Date: 2007-09-26 05:46:45 -0400 (Wed, 26 Sep 2007)
New Revision: 11650
Modified:
torflow/trunk/TorCtl/PathSupport.py
torflow/trunk/op-addon.py
torflow/trunk/pathrc.example
Log:
Introducing 'simulation'-mode: This can be used for generating many paths
(using any method of selecting nodes) in order to analyze the degree of the
provided anonymity.
Modified: torflow/trunk/TorCtl/PathSupport.py
===================================================================
--- torflow/trunk/TorCtl/PathSupport.py 2007-09-26 05:26:40 UTC (rev 11649)
+++ torflow/trunk/TorCtl/PathSupport.py 2007-09-26 09:46:45 UTC (rev 11650)
@@ -837,6 +837,11 @@
self.sorted_r.sort(lambda x, y: cmp(y.bw, x.bw))
for i in xrange(len(self.sorted_r)): self.sorted_r[i].list_rank = i
+ def build_path(self):
+ """ Get a path from the SelectionManager's PathSelector, can be used
+ e.g. for generating paths without actually creating any circuits """
+ return self.selmgr.path_selector.build_path(self.selmgr.pathlen)
+
def attach_stream_any(self, stream, badcircs):
# Newnym, and warn if not built plus pending
unattached_streams = [stream]
Modified: torflow/trunk/op-addon.py
===================================================================
--- torflow/trunk/op-addon.py 2007-09-26 05:26:40 UTC (rev 11649)
+++ torflow/trunk/op-addon.py 2007-09-26 09:46:45 UTC (rev 11650)
@@ -29,13 +29,23 @@
DATADIR = "data/op-addon/"
# Our IP-address
IP = None
+# Simulation modus
+SIMULATE = False
# Try to get the config-file from the commandline
if len(sys.argv) == 1:
CONFIG_FILE = "pathrc.example"
elif len(sys.argv) == 2:
CONFIG_FILE = sys.argv[1]
-else:
+# Check if '--simulate' is given
+elif len(sys.argv) == 3 or len(sys.argv) == 4:
+ if sys.argv[2] == "--simulate":
+ CONFIG_FILE = sys.argv[1]
+ SIMULATE = True
+ else:
+ plog("ERROR", "Unknown argument: '" + sys.argv[2] + "' exiting.")
+ sys.exit(0)
+else:
plog("ERROR", "Too many arguments, exiting.")
sys.exit(0)
@@ -50,7 +60,7 @@
plog("ERROR", "Config file '" + CONFIG_FILE + "' does not exist, exiting.")
sys.exit(0)
-# Sections
+# Configuration sections
HOST_PORT = "HOST_PORT"
CIRC_MANAGEMENT = "CIRC_MANAGEMENT"
NODE_SELECTION = "NODE_SELECTION"
@@ -60,9 +70,10 @@
MODEL = "MODEL"
# Measure the circuits
-measure_circs = config.getboolean(RTT, "measure_circs")
-if measure_circs:
- import socks
+ping_circs = config.getboolean(RTT, "ping_circs")
+network_model = False
+if ping_circs:
+ import socks
# Hosts and ports to use for ping streams
socks_host = config.get(RTT, "socks_host")
socks_port = config.getint(RTT, "socks_port")
@@ -138,9 +149,9 @@
## Connection #################################################################
-class Connection(TorCtl.Connection):
+class Connection(PathSupport.Connection):
""" Connection-class that uses the RTTCircuit-class
- TODO: add the CircuitClass to be used somewhere """
+ TODO: add the circuit class to be used """
def build_circuit(self, pathlen, path_sel):
circ = Circuit()
circ.path = path_sel.build_path(pathlen)
@@ -170,18 +181,18 @@
self.median = 0.0
def add_value(self, value):
- # Append value
+ """ Add a value to the stats """
self.values.append(value)
# Set min & max
if self.min == 0: self.min = value
elif self.min > value: self.min = value
if self.max < value: self.max = value
# Refresh everything
- self.mean = self.get_mean()
- self.dev = self.get_dev()
- self.median = self.get_median()
+ self.mean = self._mean()
+ self.dev = self._dev()
+ self.median = self._median()
- def get_mean(self):
+ def _mean(self):
""" Compute mean from the values """
if len(self.values) > 0:
sum = reduce(lambda x, y: x+y, self.values, 0.0)
@@ -189,24 +200,24 @@
else:
return 0.0
- def get_dev(self):
+ def _dev(self):
""" Return the stddev of the values """
if len(self.values) > 1:
- mean = self.get_mean()
+ mean = self._mean()
sum = reduce(lambda x, y: x + ((y-mean)**2.0), self.values, 0.0)
s = math.sqrt(sum/(len(self.values)-1))
return s
else:
return 0.0
- def get_median(self):
+ def _median(self):
""" Return the median of the values """
if len(self.values) > 0:
values = copy.copy(self.values)
values.sort()
return values[(len(values)-1)/2]
else: return 0.0
-
+
## CircuitBuildingStats #######################################################
class CircuitBuildingStats(Stats):
@@ -263,6 +274,9 @@
self.age = 0 # Age in rounds
self.timeout_counter = 0 # Timeout limit
self.rtt_created = False # Created from the model
+ # XXX: BW stuff
+ self.bw = 0
+ self.bw_tested = False
def add_rtt(self, rtt):
""" Add a new value and refresh stats and current """
@@ -290,6 +304,7 @@
s += str(self.current_rtt) + " (" + str(self.stats.median) + "/"
s += str(self.stats.mean) + "/" + str(self.stats.dev) + ")"
if self.rtt_created: s += "*"
+ if self.bw > 0: s+= "\n\t --> bw = " + str(self.bw) + " byte/s"
return s
class Stream(PathSupport.Stream):
@@ -297,6 +312,7 @@
def __init__(self, sid, host, port, kind):
PathSupport.Stream.__init__(self, sid, host, port, kind)
self.hop = None # Save hop if this is a ping, hop=None is complete circ
+ self.bw_timestamp = None # Timestamp of the last stream_bw event
## NetworkModel ###############################################################
@@ -329,15 +345,17 @@
self.path = path[1:len(path)]
# Compute the expected RTT
self.rtt = reduce(lambda x,y: x + y.current_rtt, self.links, 0.0)
- self.min_bw = 0 # Minimum bw of routers in path
- self.ranking_index = None # Index computed from bw and RTT
+ self.rtt_score = 0 # RTT score
+ self.bw_score = 0 # BW score
+ self.min_bw = 0 # Minimum BW of routers in self.path
+ self.ranking_index = None # Index computed from BW and RTT
def to_string(self):
""" Create a string for printing out information """
s = ""
for l in self.links:
s += str(l.src) + "--" + l.dest + " (" + str(l.current_rtt) + ") " + ", "
- return s + "--> " + str(self.rtt) + " sec"
+ return s + "--> " + str(self.rtt) + " sec"
class NetworkModel:
""" This class is used to record measured RTTs of single links in a model
@@ -346,8 +364,7 @@
""" Constructor: pass the list of routers """
self.pickle_path = DATADIR + "network-model.pickle"
self.logfile = None # FileHandler(DATADIR + "proposals")
- # For generating proposals
- self.proposals = [] # Current list of circ-proposals
+ self.proposals = [] # Current list of path proposals
self.prefixes = {} # Prefixes for DFS
self.routers = routers # Link to the router-list
self.target_host = None
@@ -425,12 +442,12 @@
self.up_to_date = False
def update(self):
- """ Update model with a given list of routers """
+ """ Update model with the current list of routers """
nodes = self.graph.nodes()
for id in nodes:
if not id in self.routers:
if id:
- plog("INFO", "Router with id " + id +
+ plog("INFO", "Router with ID " + id +
" is not known, deleting node ..")
self.delete_node(id)
plog("INFO", "Updated model with current router-list")
@@ -443,6 +460,8 @@
self.target_port = port
self.max_rtt = max_rtt
self.up_to_date = False
+ plog("INFO", "Set the target to "+self.target_host+":"+
+ str(self.target_port))
def generate_proposals(self):
""" Call visit() on the root-node """
@@ -455,7 +474,7 @@
self.visit(None, [])
self.up_to_date = True
plog("INFO", "Generating " + str(len(self.proposals)) +
- " proposals took us " + str(time.time()-start) +
+ " proposals took " + str(time.time()-start) +
" seconds [max_rtt=" + str(self.max_rtt) + "]")
def get_link_info(self, path):
@@ -486,7 +505,92 @@
for n in self.graph[node]:
if n not in self.prefixes[i]:
self.visit(n, copy.copy(self.prefixes[i]), i+1)
+
+ def keys_to_routers(self, keys):
+ """ See if we know the routers specified by keys and return them """
+ routers = []
+ for id in keys:
+ if id in self.routers:
+ routers.append(self.routers[id])
+ else:
+ plog("INFO", "We do not know about a router having ID " + id)
+ try:
+ self.model.delete_node(id)
+ except:
+ plog("ERROR", "Could not delete router with ID " + id)
+ if len(routers) == len(keys):
+ return routers
+
+ def _set_min_bw(self):
+ """ Find the smallest advertised bw of the routers in each proposal """
+ for p in self.proposals:
+ # Get the routers
+ r_path = self.keys_to_routers(p.path)
+ if r_path:
+ # Find min(bw_i)
+ bw = []
+ for r in r_path:
+ bw.append(r.bw)
+ p.min_bw = min(bw)
+ else:
+ self.proposals.remove(p)
+ plog("DEBUG", "Could not find the routers, removed ..")
+ def update_ranking(self, rtt_weight, bw_weight):
+ """ Compute a ranking for each path proposal using
+ measured RTTs and bandwidth from the descriptors """
+ start = time.time()
+ # High bandwidths get high scores
+ if bw_weight > 0:
+ self._set_min_bw()
+ sort_list(self.proposals, lambda x: x.min_bw)
+ plog("DEBUG", "MIN_BWs of proposals between: " +
+ str(self.proposals[0].min_bw) + " and " +
+ str(self.proposals[len(self.proposals)-1].min_bw))
+ i = 1
+ for p in self.proposals:
+ p.bw_score = i
+ i += 1
+ # Low Latencies get high scores
+ if rtt_weight > 0:
+ sort_list(self.proposals, lambda x: x.rtt)
+ plog("DEBUG", "RTTs of proposals between: " + str(self.proposals[0].rtt) +
+ " and " + str(self.proposals[len(self.proposals)-1].rtt))
+ i = len(self.proposals)
+ for p in self.proposals:
+ p.rtt_score = i
+ i -= 1
+ # Compute weights from both of the values
+ for p in self.proposals:
+ # Calculate ranking index based on both scores
+ p.ranking_index = (rtt_weight*p.rtt_score)+(bw_weight*p.bw_score)
+ sort_list(self.proposals, lambda x: x.ranking_index)
+ plog("DEBUG", "Ranking indices of proposals between: " +
+ str(self.proposals[0].ranking_index) + " and " +
+ str(self.proposals[len(self.proposals)-1].ranking_index))
+ plog("INFO", "Updating ranking indices of proposals took "
+ + str(time.time()-start) + " sec")
+
+ def weighted_selection(self, weight):
+ """ Select a proposal in a probabilistic way """
+ choice = None
+ # Compute the sum of weights
+ sum = 0
+ for p in self.proposals:
+ sum += weight(p)
+ plog("DEBUG", "Sum of all weights is " + str(sum))
+ # Choose a random number from [0,sum-1]
+ i = random.randint(0, sum-1)
+ plog("DEBUG", "Chosen random number is " + str(i))
+ # Go through the proposals and subtract
+ for p in self.proposals:
+ i -= weight(p)
+ if i < 0:
+ choice = p
+ plog("DEBUG", "Chosen object with ranking " +
+ str(weight(choice)))
+ return choice
+
def print_info(self):
""" Create a string holding info and the proposals for printing """
out = str(self.graph.info())
@@ -513,10 +617,9 @@
self.setup_logger = None # FileHandler(DATADIR + "circ-setup-durations")
if TESTING_MODE:
self.testing_logger = FileHandler(DATADIR + "circ-data")
-
+ self.bw_queue = Queue.Queue() # circ_ids to bw-test
# Queue containing circs to be tested
self.ping_queue = Queue.Queue() # (circ_id, hop)-pairs
-
if use_model:
PathSupport.StreamHandler.__init__(self, c, selmgr, 0, RouterClass)
self.model = NetworkModel(self.routers)
@@ -525,7 +628,6 @@
else:
self.model = None
PathSupport.StreamHandler.__init__(self, c, selmgr, num_circs, RouterClass)
-
# Sorted circuit list
self.sorted_circs = []
# Start the Pinger
@@ -554,7 +656,7 @@
""" To be called when tests are finished for writing
any interesting values to a file before closing circ """
self.testing_logger.append(str(circ.setup_duration) + "\t" +
- str(circ.stats.mean))
+ str(circ.bw/1024) + "\t" + str(circ.stats.mean))
line_count = self.testing_logger.get_line_count()
if line_count >= num_records:
plog("INFO", "Enough records, exiting. (line_count = " +
@@ -562,26 +664,30 @@
# TODO: How to kill the main thread from here?
sys.exit(1)
- def enqueue_pings(self):
+ def start_round(self):
""" schedule_immediate from pinger before triggering the initial ping """
print("")
self.refresh_sorted_list()
# TODO: Check if there are any circs, else set 'frequency' to 10?
circs = self.circuits.values()
for c in circs:
- if c.built:
- # Get id of c
- id = c.circ_id
- if self.model:
- # Enqueue every hop
- path_len = len(c.path)
- for i in xrange(1, path_len):
- self.ping_queue.put((id, i))
- plog("DEBUG", "Enqueued circuit " + str(id) + " hop " + str(i))
- # And for the whole circuit ...
- self.ping_queue.put((id, None))
- plog("DEBUG", "Enqueued circuit " + str(id) + " hop None")
+ self.enqueue_circ(c)
+ def enqueue_circ(self, c):
+ """ Enqueue a circuit for measuring RTT """
+ if c.built:
+ # Get id of c
+ id = c.circ_id
+ if self.model:
+ # Enqueue every hop
+ path_len = len(c.path)
+ for i in xrange(1, path_len):
+ self.ping_queue.put((id, i))
+ plog("DEBUG", "Enqueued circuit " + str(id) + " hop " + str(i))
+ # And for the whole circuit ...
+ self.ping_queue.put((id, None))
+ plog("DEBUG", "Enqueued circuit " + str(id) + " hop None")
+
def attach_ping(self, stream):
""" Attach a ping stream to its circuit """
if self.ping_queue.empty():
@@ -594,8 +700,7 @@
if self.model:
self.model.print_info()
# Enqueue again all circs
- self.enqueue_pings()
-
+ self.start_round()
else:
# Get the info and extract
ping_info = self.ping_queue.get()
@@ -782,128 +887,54 @@
# Check ratio if we would add circ from model
trad = self.get_trad_circs()
ratio = trad/(len(self.circuits.values())+1.)
- plog("DEBUG","Expected Ratio = " + str(ratio) +
+ plog("DEBUG","Expected Ratio: " + str(ratio) +
" >= " + str(min_ratio) + " ?")
if ratio >= min_ratio:
if self.create_circ_from_model(host, port):
return
plog("INFO", "Not enough proposals [min_proposals=" + str(min_proposals) + "]")
-
# Create a circuit using the backup-method
- plog("DEBUG", "Creating circuit with the backup-method")
+ plog("INFO", "Creating circuit with the backup-method")
PathSupport.CircuitHandler.build_circuit(self, host, port)
- # Path selection from the model =============================================
def create_circ_from_model(self, host, port):
# Set the target
self.model.set_target(host, port, max_rtt)
if not self.model.up_to_date:
self.model.generate_proposals()
- self.set_min_bw(self.model.proposals)
- # Get the proposals and compute ranking
- proposals = self.model.proposals
- if len(proposals) >= min_proposals:
+ plog("DEBUG", "Current number of proposals is "+
+ str(len(self.model.proposals)))
+ if len(self.model.proposals) >= min_proposals:
# Give weights for single scores
- self.update_ranking(proposals, 1, 1)
- # As long as there are enough
- while len(proposals) >= min_proposals:
+ self.model.update_ranking(1, 0)
+ # As long as there are enough
+ while len(self.model.proposals) >= min_proposals:
- # Uniform:
- # choice = random.choice(proposals)
- # Fastest First:
- # proposals = sort_list(proposals, lambda x: x.rtt)
- # choice = proposals[0]
-
- # Probabilistic selection:
- choice = self.weighted_selection(proposals, lambda x: x.ranking_index)
+ # Uniform:
+ # choice = random.choice(self.model.proposals)
+ # Fastest First:
+ # proposals = sort_list(self.model.proposals, lambda x: x.rtt)
+ # choice = proposals[0]
- # Convert ids to routers
- r_path = self.keys_to_routers(choice.path)
- if r_path and self.path_is_ok(r_path, host, port):
- plog("INFO", "Chosen proposal: " + choice.to_string())
- try:
- circ = self.c.build_circuit_from_path(r_path)
- circ.rtt_created = True
- self.circuits[circ.circ_id] = circ
- plog("INFO", "Created circ from model: " + str(circ.circ_id))
- return True
- except TorCtl.ErrorReply, e:
- plog("NOTICE", "Error building circuit: " + str(e.args))
- else:
- proposals.remove(choice)
+ # Probabilistic selection:
+ choice = self.model.weighted_selection(lambda x: x.ranking_index)
- def set_min_bw(self, proposals):
- """ Find the smallest advertised bw of the routers in each proposal """
- for p in proposals:
- # Get the routers
- r_path = self.keys_to_routers(p.path)
- if r_path:
- # Find min(bw_i)
- bw = []
- for r in r_path:
- bw.append(r.bw)
- p.min_bw = min(bw)
- else:
- proposals.remove(p)
- plog("DEBUG", "Could not find the routers, removed ..")
-
- def weighted_selection(self, proposals, weight):
- """ Select a proposal in a probabilistic way """
- choice = None
- # Compute the sum of weights
- sum = 0
- for p in proposals:
- sum += weight(p)
- plog("DEBUG", "Sum of all weights is " + str(sum))
- # Choose a random number from [0,sum-1]
- i = random.randint(0, sum-1)
- plog("DEBUG", "Chosen random number is " + str(i))
- # Go through the proposals and subtract
- for p in proposals:
- i -= weight(p)
- if i < 0:
- choice = p
- plog("DEBUG", "Chosen path with ranking " +
- str(weight(choice)))
- return choice
-
- def update_ranking(self, proposals, rtt_weight, bw_weight):
- """ Compute a ranking for each path-proposal using
- measured RTTs and bandwidth from the descriptors """
- start = time.time()
- # High bandwidths get high scores
- sort_list(proposals, lambda x: x.min_bw)
- plog("DEBUG", "MIN_BWs of proposals between: " + str(proposals[0].min_bw) +
- " and " + str(proposals[len(proposals)-1].min_bw))
- i = 1
- for p in proposals:
- p.bw_score = i
- i += 1
- # Low Latencies get high scores
- sort_list(proposals, lambda x: x.rtt)
- plog("DEBUG", "RTTs of proposals between: " + str(proposals[0].rtt) +
- " and " + str(proposals[len(proposals)-1].rtt))
- i = len(proposals)
- for p in proposals:
- p.rtt_score = i
- i -= 1
- # Compute weights from both of the values
- for p in proposals:
- # Calculate ranking index based on both scores
- p.ranking_index = (rtt_weight*p.rtt_score)+(bw_weight*p.bw_score)
- sort_list(proposals, lambda x: x.ranking_index)
- plog("DEBUG", "Ranking indices of proposals between: " + str(proposals[0].ranking_index)
- + " and " + str(proposals[len(proposals)-1].ranking_index))
- plog("INFO", "Updating ranking indices of proposals took "
- + str(time.time()-start) + " sec")
+ # Convert ids to routers
+ r_path = self.model.keys_to_routers(choice.path)
+ if r_path and self.path_is_ok(r_path):
+ plog("INFO", "Chosen proposal: " + choice.to_string())
+ try:
+ circ = self.c.build_circuit_from_path(r_path)
+ circ.rtt_created = True
+ self.circuits[circ.circ_id] = circ
+ plog("INFO", "Created circ from model: " + str(circ.circ_id))
+ return True
+ except TorCtl.ErrorReply, e:
+ plog("NOTICE", "Error building circuit: " + str(e.args))
+ else:
+ self.model.proposals.remove(choice)
# Helper functions ==========================================================
- def established(self, circ_list):
- """ Check if there is at least one circuit established (NOT USED) """
- for c in circ_list:
- if c.built:
- return True
-
def get_trad_circs(self):
""" Count the circuits with rtt_created == False """
trad_circs = 0
@@ -912,41 +943,27 @@
trad_circs += 1
return trad_circs
- def path_is_ok(self, path, host, port):
+ def path_is_ok(self, path):
""" Check if there is currently a circuit with the given path (Routers) """
- for c in self.circuits.values():
- if c.path == path:
- plog("ERROR", "Proposed circuit already exists")
- return False
- return True
+ if path:
+ for c in self.circuits.values():
+ if c.path == path:
+ plog("ERROR", "Proposed circuit already exists")
+ return False
+ return True
- def keys_to_routers(self, keys):
- """ See if we know the routers specified by keys and return them """
- routers = []
- for id in keys:
- if id in self.routers:
- routers.append(self.routers[id])
- else:
- plog("INFO", "We do not know about a router having ID " + id)
- try:
- self.model.delete_node(id)
- except:
- plog("ERROR", "Could not delete router with ID " + id)
- if len(routers) == len(keys):
- return routers
-
## Pinger #####################################################################
class Pinger(threading.Thread):
""" Separate thread that triggers the Socks4-connections for pings """
def __init__(self, ping_handler):
self.handler = ping_handler # the PingHandler
- threading.Thread.__init__(self) # call the thread-constructor
+ threading.Thread.__init__(self)
def run(self):
""" The run()-method """
time.sleep(initial_interval)
- self.handler.schedule_immediate(lambda x: x.enqueue_pings())
+ self.handler.schedule_immediate(lambda x: x.start_round())
while self.isAlive():
self.ping()
time.sleep(frequency)
@@ -970,12 +987,20 @@
## End of Classes #############################################################
-def connect(host, port):
+def connect():
""" Return a connection to Tor's control port """
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect((host, port))
- return Connection(sock)
-
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((config.get(HOST_PORT, "control_host"),
+ config.getint(HOST_PORT, "control_port")))
+ conn = Connection(sock)
+ conn.authenticate()
+ #conn.debug(file("control.log", "w"))
+ except socket.error, e:
+ plog("ERROR", "Could not connect to Tor process .. running?")
+ sys.exit(-1)
+ return conn
+
def setup_location(conn):
""" Setup a router object representing this proxy """
#global path_config
@@ -997,24 +1022,18 @@
def configure(conn):
""" Set events and options """
conn.set_events([TorCtl.EVENT_TYPE.STREAM,
- TorCtl.EVENT_TYPE.CIRC,
- TorCtl.EVENT_TYPE.ADDRMAP,
- TorCtl.EVENT_TYPE.NS,
- TorCtl.EVENT_TYPE.NEWDESC], True)
+ TorCtl.EVENT_TYPE.CIRC,
+ TorCtl.EVENT_TYPE.STREAM_BW,
+ TorCtl.EVENT_TYPE.ADDRMAP,
+ TorCtl.EVENT_TYPE.NS,
+ TorCtl.EVENT_TYPE.NEWDESC], True)
# Set options: We attach streams now & build circuits
conn.set_option("__LeaveStreamsUnattached", "1")
conn.set_option("__DisablePredictedCircuits", "1")
def startup(argv):
- try:
- # Connect to Tor process
- conn = connect(config.get(HOST_PORT, "control_host"),
- config.getint(HOST_PORT, "control_port"))
- conn.authenticate()
- #conn.debug(file("control.log", "w"))
- except socket.error, e:
- plog("ERROR", "Could not connect to Tor process .. running?")
- return
+ # Connect to Tor process
+ conn = connect()
# Setup our location
setup_location(conn)
# Configure myself
@@ -1022,13 +1041,13 @@
# Get the size of the circuit-pool from config
num_circs = config.getint(CIRC_MANAGEMENT, "idle_circuits")
# Set an EventHandler to the connection
- if measure_circs:
+ if ping_circs:
if network_model:
handler = PingHandler(conn, __selmgr, num_circs,
GeoIPSupport.GeoIPRouter, True)
else:
handler = PingHandler(conn, __selmgr, num_circs,
- GeoIPSupport.GeoIPRouter)
+ GeoIPSupport.GeoIPRouter)
else:
# No pings, only a StreamHandler
handler = PathSupport.StreamHandler(conn, __selmgr, num_circs,
@@ -1040,20 +1059,157 @@
time.sleep(60)
except KeyboardInterrupt:
# XXX: Schedule this?
- if measure_circs:
+ if ping_circs:
if network_model:
handler.model.save_graph()
- # TODO: Stop other threads, close circuits
cleanup(conn)
sys.exit(1)
def cleanup(conn):
""" To be called on exit """
+ # TODO: Stop other threads and close circuits
plog("INFO", "Cleaning up...")
conn.set_option("__LeaveStreamsUnattached", "0")
conn.set_option("__DisablePredictedCircuits", "0")
conn.close()
+def simulate(n):
+ """ Simulate circuit creations """
+ plog("INFO", "Starting simulation ..")
+ # Connect to Tor process
+ conn = connect()
+ setup_location(conn)
+ # The generated paths
+ path_list = []
+ # Instantiate a PathBuilder
+ path_builder = PathSupport.PathBuilder(conn, __selmgr, GeoIPSupport.GeoIPRouter)
+ plog("INFO", "Creating "+str(n)+" paths")
+ if network_model:
+ model = NetworkModel(path_builder.routers)
+ model.set_target("255.255.255.255", 80, max_rtt)
+ model.generate_proposals()
+ # Give weights for single scores (RTT, advertised BW)
+ model.update_ranking(1, 1)
+ while n > 0:
+ # Probabilistic selection
+ choice = model.weighted_selection(lambda x: x.ranking_index)
+ # Convert ids to routers
+ path = model.keys_to_routers(choice.path)
+ path_list.append(path)
+ n -= 1
+ else:
+ while n > 0:
+ path = path_builder.build_path()
+ path_list.append(path)
+ n -= 1
+ # Evaluate the generated paths and exit
+ evaluate(path_list)
+ cleanup(conn)
+ sys.exit(1)
+
+def evaluate(path_list):
+ """ Currently evaluates only lists of 3-hop paths """
+ import sets
+ entries = sets.Set()
+ middles = sets.Set()
+ exits = sets.Set()
+ ee_combinations = {}
+ # Count occurrences of routers on single positions and
+ # different combinations of [entry,exit]
+ for p in path_list:
+ entries.add(p[0])
+ middles.add(p[1])
+ exits.add(p[2])
+ if not ee_combinations.has_key((p[0], p[2])):
+ ee_combinations[(p[0], p[2])] = 1
+ else:
+ ee_combinations[(p[0], p[2])] += 1
+ # General logging
+ logfile = FileHandler(DATADIR+"simulation")
+ output = [str(len(entries)), str(len(middles)), str(len(exits))]
+ logfile.append(str(len(path_list))+" paths: "+" - ".join(output))
+ # Verbose about numbers of chosen nodes
+ plog("INFO", "Different nodes [entry/middle/exit]: "+"/".join(output))
+ # And combinations of entries and exits
+ plog("INFO", "Different [entry,exit]-combinations: " +
+ str(len(ee_combinations)))
+ # Get list of the counters and sort it
+ counters = ee_combinations.values()
+ sort_list(counters, lambda x: x)
+ # Log probabilities
+ probs = []
+ output = ""
+ for i in counters:
+ if i > 0:
+ # Calculate probability from counter i
+ prob = float(i)/len(path_list)
+ # Add it to the list
+ probs.append(prob)
+ # And add a new line to the output
+ line = str(i)+"\t"+str(prob)+"\n"
+ output += line
+ prob_logger = FileHandler(DATADIR+"ee_probs")
+ prob_logger.write(output)
+ # Determine entropies
+ m_entropy = get_max_entropy(len(path_list))
+ entropy = get_entropy(probs)
+ d = entropy/m_entropy
+ plog("INFO", "Maximum entropy: "+str(m_entropy))
+ plog("INFO", "Entropy of this sample: "+str(entropy))
+ plog("INFO", "Degree of anonymity: "+str(d))
+ # Calculate percentiles from the sorted list
+ percentile_logger = FileHandler(DATADIR+"percentiles")
+ percentile_logger.write("")
+ percents = []
+ i = counters.pop(0)
+ n = 1
+ while len(counters)>0:
+ new = counters.pop(0)
+ if new == i:
+ n += 1
+ else:
+ percentile = (float(n*i)/len(path_list))*100
+ percents.append(percentile)
+ prob = float(i)/len(path_list)
+ plog("DEBUG", str(percentile)+
+ " percent of the paths having ee_prob = "+str(prob))
+ percentile_logger.append(str(percentile)+"\t"+str(prob))
+ i = new
+ n = 1
+ percentile = (float(n*i)/len(path_list))*100
+ percents.append(percentile)
+ prob = float(i)/len(path_list)
+ plog("DEBUG", str(percentile)+
+ " percent of the paths having ee_prob = "+str(prob))
+ percentile_logger.append(str(percentile)+"\t"+str(prob))
+ # Checking percentiles
+ sum = reduce(lambda x, y: x+y, percents, 0.0)
+ plog("DEBUG", "(Sum of percentiles is "+str(sum)+")")
+
+def get_entropy(probs):
+ """ Return the entropy of a given list of probabilities """
+ # Check if the sum is 1
+ sum = reduce(lambda x, y: x+y, probs, 0.0)
+ plog("DEBUG", "(Sum of probs is "+str(sum)+")")
+ # Compute the entropy
+ entropy = -reduce(lambda x, y: x+(y*math.log(y,2)), probs, 0.0)
+ return entropy
+
+def get_max_entropy(n):
+ """ Calculate the maximum entropy in a sample of size n """
+ sum = 0.0
+ p = 1/float(n)
+ for i in range(1,n+1):
+ sum += p*math.log(p,2)
+ max_entropy = -sum
+ return max_entropy
+
if __name__ == '__main__':
plog("INFO", "OP-Addon v" + VERSION)
- startup(sys.argv)
+ if SIMULATE:
+ if len(sys.argv) == 3:
+ simulate(10)
+ else:
+ simulate(int(sys.argv[3]))
+ else:
+ startup(sys.argv)
Modified: torflow/trunk/pathrc.example
===================================================================
--- torflow/trunk/pathrc.example 2007-09-26 05:26:40 UTC (rev 11649)
+++ torflow/trunk/pathrc.example 2007-09-26 09:46:45 UTC (rev 11650)
@@ -1,6 +1,6 @@
[HOST_PORT]
-# Set Host and Port where Tor is
+# Set the host and port where Tor is
# listening for control-connections
control_host = 127.0.0.1
control_port = 9051
@@ -73,7 +73,6 @@
# Number of latency-tests per circuit (int: 0-n)
num_rtt_tests = 5
-
# Amount of circuits to test (int)
num_records = 300
@@ -81,7 +80,7 @@
# Measure latencies of complete circuits
# yes|no
-measure_circs = yes
+ping_circs = yes
# Tor socks-properties
socks_host = 127.0.0.1
@@ -111,10 +110,8 @@
# RTT-threshhold in seconds when creating circs (float):
# 0: no threshhold, choose from all proposals
max_rtt = 1
-
# Minimum number of proposals to choose from (int)
min_proposals = 100
-
# Min ratio of circs created with the backup-method,
# controls growing of the model (float in [0,1])
# 0: no growing
More information about the tor-commits
mailing list