[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r10602: Switched back to one socks connection for each ping request, (torflow/trunk)



Author: renner
Date: 2007-06-14 10:29:25 -0400 (Thu, 14 Jun 2007)
New Revision: 10602

Modified:
   torflow/trunk/op-addon.py
Log:

  Switched back to one socks connection for each ping request, triggering pings at a constant rate now
  (no need for synchronization mechanisms anymore) and cleaned up CircuitHandler and StreamHandler from
  my ping related code.



Modified: torflow/trunk/op-addon.py
===================================================================
--- torflow/trunk/op-addon.py	2007-06-14 14:03:09 UTC (rev 10601)
+++ torflow/trunk/op-addon.py	2007-06-14 14:29:25 UTC (rev 10602)
@@ -10,7 +10,6 @@
 # from or-addons/alternate directory, building fast circuits from all 
 # of these infos and attaching streams to fast circuits.
 
-# TODO: import 'with'-statement for Lock objects (Python 2.5: "with some_lock: do something")
 import re
 import sys
 import copy
@@ -41,35 +40,44 @@
 ping_dummy_host = "127.0.0.1"
 ping_dummy_port = 100
 
-# Close circ after n timeouts or avg measured slownesses
-timeout_limit = 1
-slowness_limit = 3
-# Slow RTT := x seconds 
-slow = 0.7
-# Note: Tor-internal lifetime of a circuit is 10 min --> 600/sleep_interval = max-age
-# Sleep interval between working loads in sec
-sleep_interval = 5
 # No of idle circuits to build preemptively
 # TODO: Also configure ports to use
-idle_circuits = 5
+idle_circuits = 4
 
 # Measure complete circuits
 measure_circs = True
+# Note: Tor-internal lifetime of a circuit is 10 min --> 600/sleep_interval = max-age
+# Sleep interval between working loads in sec
+initial_interval = 8
+sleep_interval = 1
+# Close circ after n timeouts or avg measured slownesses
+timeout_limit = 1
+# Slow RTT := x seconds, close circs slower & create only circs faster than this
+slowness_limit = 5
+slow = 1.
+
 # Set to True if we want to measure partial circuits
-measure_partial_circs = False
+# This also enables circuit creation from the model
+measure_partial_circs = True
+# Minimum number of proposals to choose from
+min_proposals = 10
+# Min ratio of traditionally created circs
+# ensures growing of the explored subnet
+min_ratio = 1./3.
 
 # Testing mode: Close circuits after num_tests measures + 
 # involves a FileHandler to write collected data to a file
-testing_mode = False
+testing_mode = True
 # Number of tests per circuit
 num_tests = 5
 
-# Do configuration here TODO: use my_country for src
-# Set src_country below when setting up our location
-path_config = GeoIPSupport.GeoIPConfig(unique_countries = True,
-                                       src_country = None,
-				       crossings = 1,
-				       excludes = [])
+# Do geoip-configuration here
+# TODO: Set src_country below when setting up our location
+path_config = GeoIPSupport.GeoIPConfig(unique_countries = None,
+                                       entry_country = None,
+				       exit_country = None,
+				       max_crossings = 1,
+				       excludes = None)
 
 # Configure Selection Manager here!!
 # Do NOT modify this object directly after it is handed to 
@@ -86,13 +94,10 @@
       use_guards=True,
       geoip_config=path_config)
 
-# Signalize that a round has finished
-finished_event = threading.Event()
-
 ######################################### BEGIN: Connection         #####################
 
 class Connection(TorCtl.Connection):
-  """ Connection class that uses my Circuit class """
+  """ Connection class that uses my own Circuit class """
   def build_circuit(self, pathlen, path_sel):
     circ = Circuit()
     if pathlen == 1:
@@ -109,7 +114,7 @@
     return circ
 
   def build_circuit_from_path(self, path):
-    """ Build circuit using a given path shall be used to build circs from NetworkModel """
+    """ Build circuit using a given path, used to build circs from NetworkModel """
     circ = Circuit()
     circ.rtt_created = True
     # Set path to circuit
@@ -123,6 +128,7 @@
 ######################################### Stats                    #####################
 
 class Stats:
+  """ Statistics class that can be used for recording stats about measured RTTs """
   def __init__(self):
     self.values = []
     self.min = 0.0
@@ -205,13 +211,13 @@
     self.age += 1
 
   def to_string(self):
-    """ Create a string representation """
+    """ Create a current string representation """
     s = "Circuit " + str(self.circ_id) + ": "
     for r in self.path: s += " " + r.nickname + "(" + str(r.country_code) + ")"
     if not self.built: s += " (not yet built)"
     else: s += " (age=" + str(self.age) + ")"
     if self.current_rtt: 
-      s += ": " "RTT current/median/mean/dev: "
+      s += ": " "RTT (current/median/mean/dev): "
       s += str(self.current_rtt) + "/" + str(self.stats.median) + "/"
       s += str(self.stats.mean) + "/" + str(self.stats.dev)
     if self.rtt_created: s += "*"
@@ -243,11 +249,11 @@
     self.stats.add_value(rtt)
 
 class PathProposal:
-  """ Instances of this class are path-proposals """
+  """ Instances of this class are path-proposals found in the model """
   def __init__(self, links, path):
     # This is a list of LinkInfo objects
     self.links = links
-    # Also save the path for passing to build_circuit, cut off ROOT
+    # Also save the path for passing to build_circuit, cut off ROOT here
     self.path = path[1:len(path)]
     # Compute the expected RTT (from current value?)
     self.rtt = reduce(lambda x,y: x + y.current_rtt, self.links, 0.0)
@@ -289,10 +295,14 @@
     # Reset list of proposals and prefixes for DFS
     self.proposals = []
     self.prefixes = {}
+    # Measure for info
+    start = time.time()
     # Start the search
     self.visit(self.root, [])
     # Sort proposals for their RTTs
     sort_list(self.proposals, lambda x: x.rtt)
+    # Some logging
+    plog("DEBUG", "Finding the proposals and sorting them took us " + str(time.time()-start) + " seconds")
     # Print all of them for debugging/info
     for p in self.proposals:
       print(p.to_string())
@@ -330,30 +340,44 @@
 
 ######################################### BEGIN: EventHandlers     #####################
 
-# TODO: Store the number of circuits here
 class CircuitHandler(PathSupport.PathBuilder):
   """ CircuitHandler that extends from PathBuilder """
-  def __init__(self, c, selmgr):
+  def __init__(self, c, selmgr, num_circuits):
     # Init the PathBuilder
-    PathSupport.PathBuilder.__init__(self, c, selmgr, GeoIPSupport.GeoIPRouter)    
-    self.circs_sorted = []	# list of circs sorted by mean RTT
-    self.check_circuit_pool()	# bring up the pool of circs
- 
+    PathSupport.PathBuilder.__init__(self, c, selmgr, GeoIPSupport.GeoIPRouter)
+    self.num_circuits = num_circuits    # size of the circuit pool
+    self.check_circuit_pool()	        # bring up the pool of circs
+    self.sorted_circs = None		# attribute to hold a sorted list of the circuits
+
   def check_circuit_pool(self):
     """ Init or check the status of our pool of circuits """
     # Get current number of circuits
     n = len(self.circuits.values())
-    i = idle_circuits-n
+    i = self.num_circuits - n
     if i > 0:
       plog("INFO", "Checked pool of circuits: we need to build " + str(i) + " circuits")
-    # Schedule (idle_circuits-n) circuit-buildups
-    while (n < idle_circuits):      
+    # Schedule (num_circs - n) circuit-buildups
+    while (n < self.num_circuits):      
       self.build_idle_circuit()
       plog("DEBUG", "Scheduled circuit No. " + str(n+1))
       n += 1
 
+  def close_circuit(self, id):
+    """ Try to close a circuit with given id """
+    self.circuits[id].closed = True
+    try: self.c.close_circuit(id)
+    except TorCtl.ErrorReply, e: 
+      plog("ERROR", "Failed closing circuit " + str(id) + ": " + str(e))	    
+
+  def print_circuits(self):
+    """ Print out our circuits plus some info """
+    circs = self.circuits.values()
+    plog("INFO", "We have " + str(len(circs)) + " circuits:")
+    for c in circs:
+      print("+ " + c.to_string())
+
   def check_path(self, path):
-    """ Check if we already have a circuit with this path """
+    """ Check if we currently do not have (TODO: had?) a circuit with the given path """
     for c in self.circuits.values():
       if c.path == path:
         return False
@@ -364,26 +388,7 @@
     circ = None
     while circ == None:
       try:
-        if measure_partial_circs:
-	  # Get the proposals RTT <= 0.5
-	  proposals = self.model.check_proposals(slow)
-	  # TODO: Ensure we also create new paths (check number of circs with rtt_created)
-	  # TODO: Check if we have > m proposals
-	  while len(proposals) > 0:
-	    choice = random.choice(proposals)
-	    # Check if we already have a circ with this path
-	    if self.check_path(choice.path):
-	      plog("INFO", "Chosen proposal: " + choice.to_string())
-	      circ = self.c.build_circuit_from_path(choice.path)
-	      self.circuits[circ.circ_id] = circ
-	      return
-	    else:
-	      plog("DEBUG", "Proposed circuit already exists")
-	      # Remove from the proposals
-	      proposals.remove(choice)
-	  plog("DEBUG", "Falling back to normal path selection") 
-
-        # Build the circuit
+        # Build the circuit, configure which ports to use
 	self.selmgr.set_target("255.255.255.255", 80)
         circ = self.c.build_circuit(self.selmgr.pathlen, self.selmgr.path_selector)
 	self.circuits[circ.circ_id] = circ
@@ -391,18 +396,6 @@
         # FIXME: How come some routers are non-existant? Shouldn't
         # we have gotten an NS event to notify us they disappeared?
         plog("NOTICE", "Error building circuit: " + str(e.args))
-
-  def print_circuits(self):
-    """ Print out our circuits plus some info """
-    circs = self.circuits.values()
-    plog("INFO", "We have " + str(len(circs)) + " circuits:")
-    for c in circs:
-      print("+ " + c.to_string())
-
-  def refresh_sorted_list(self):
-    """ Sort the list for their mean RTTs """
-    self.circs_sorted = sort_list(self.circuits.values(), lambda x: x.stats.mean)
-    plog("DEBUG", "Refreshed sorted list of circuits")
  
   def circ_status_event(self, c):
     """ Handle circuit status events """
@@ -432,8 +425,6 @@
       for stream in circ.pending_streams:
 	plog("DEBUG", "Finding new circ for " + str(stream.strm_id))
         self.attach_stream_any(stream, stream.detached_from)
-      # Refresh the list 
-      self.refresh_sorted_list()
       # Check if there are enough circs
       self.check_circuit_pool()
       return
@@ -459,11 +450,9 @@
 
 class StreamHandler(CircuitHandler):
   """ This is a StreamHandler that extends from the CircuitHandler """
-  def __init__(self, c, selmgr):    
+  def __init__(self, c, selmgr, num_circs):    
     # Call constructor of superclass
-    CircuitHandler.__init__(self, c, selmgr)
-    # NEWNYM is needed for testing bandwidth
-    #self.new_nym = True
+    CircuitHandler.__init__(self, c, selmgr, num_circs)
  
   def clear_dns_cache(self):
     """ Send signal CLEARDNSCACHE """
@@ -471,10 +460,12 @@
     for _, msg, more in lines:
       plog("DEBUG", "CLEARDNSCACHE: " + msg)
 
+  def close_stream(self, id, reason):
+    """ Close a stream with given id and reason """
+    self.c.close_stream(id, reason)
+
   def attach_stream_any(self, stream, badcircs):
     """ Attach a regular user stream """
-    # To be able to always choose the fastest: slows down attaching?
-    #self.clear_dns_cache()
     # Newnym, and warn if not built plus pending
     unattached_streams = [stream]
     if self.new_nym:
@@ -490,11 +481,13 @@
         # FIXME: Consider actually closing circ if no streams.
         self.circuits[key].dirty = True
 
-    # Choose from the sorted list
-    # TODO: We don't have a sorted list if we don't measure!
-    for circ in self.circs_sorted:
-      # Only attach if we already measured
-      if circ.built and not circ.closed and circ.circ_id not in badcircs and circ.current_rtt:
+    # Check if there is a sorted list of circs
+    if self.sorted_circs: list = self.sorted_circs
+    else: list = self.circuits.values()
+    # Choose a circuit
+    for circ in list:
+      # Check each circuit
+      if circ.built and not circ.closed and circ.circ_id not in badcircs:
         if circ.exit.will_exit_to(stream.host, stream.port):
           try:
             self.c.attach_stream(stream.strm_id, circ.circ_id)
@@ -517,8 +510,6 @@
         try:
           circ = self.c.build_circuit(self.selmgr.pathlen, self.selmgr.path_selector)
         except TorCtl.ErrorReply, e:
-          # FIXME: How come some routers are non-existant? Shouldn't
-          # we have gotten an NS event to notify us they disappeared?
           plog("NOTICE", "Error building circ: " + str(e.args))
       for u in unattached_streams:
         plog("DEBUG", "Attaching " + str(u.strm_id) + " pending build of circuit " + str(circ.circ_id))
@@ -562,8 +553,8 @@
         self.streams[s.strm_id].detached_from.append(s.circ_id)      
       # Detect timeouts on user streams
       if s.reason == "TIMEOUT":
-	# Increase a timeout counter on the stream?
-	#self.circuits[s.circ_id].timeout_counter += 1
+	# TODO: Increase a timeout counter on the stream?
+	#self.streams[s.strm_id].timeout_counter += 1
 	plog("DEBUG", "User stream timed out on circuit " + str(s.circ_id))
       # Stream was pending
       if self.streams[s.strm_id] in self.streams[s.strm_id].pending_circ.pending_streams:
@@ -596,8 +587,7 @@
         plog("NOTICE", "Failed stream " + str(s.strm_id) + " not found")
         return
       #if not s.circ_id: plog("WARN", "Stream " + str(s.strm_id) + " closed/failed from no circuit")
-      # We get failed and closed for each stream. OK to return 
-      # and let the CLOSED do the cleanup
+      # We get failed and closed for each stream. OK to return and let the CLOSED do the cleanup
       if s.status == "FAILED":
         # Avoid busted circuits that will not resolve or carry traffic
         self.streams[s.strm_id].failed = True
@@ -626,33 +616,41 @@
 
 class PingHandler(StreamHandler):
   """ This class extends the general StreamHandler to handle ping-requests """
-  def __init__(self, c, selmgr, router):
+  def __init__(self, c, selmgr, num_circs, router, partial=False):
     # Anything ping-related
     self.ping_queue = Queue.Queue()	# (circ_id, hop)-pairs
     self.start_times = {}		# dict mapping (circ_id, hop):start_time TODO: cleanup
-    # Start the Pinger that triggers the connections
-    self.pinger = Pinger(self)
-    self.pinger.setDaemon(True)
-    self.pinger.start()
     # Additional stuff for partial measurings
-    if measure_partial_circs:
+    self.partial_circs = partial
+    if self.partial_circs:
       self.router = router			# this object represents this OR
       self.model = NetworkModel(self.router)	# model for recording link-RTTs
     # Handle testing_mode
     if testing_mode:
       self.filehandler = FileHandler("data/circuits")
     # Init the StreamHandler
-    StreamHandler.__init__(self, c, selmgr)    
+    StreamHandler.__init__(self, c, selmgr, num_circs)    
+    # Start the Pinger that triggers the connections
+    self.pinger = Pinger(self)
+    self.pinger.setDaemon(True)
+    self.pinger.start()
+    # Sorted circuit list
+    self.sorted_circs = []		# list of circs sorted by mean RTT
 
+  def refresh_sorted_list(self):
+    """ Sort the list for their mean RTTs or something else? """
+    self.sorted_circs = sort_list(self.circuits.values(), lambda x: x.stats.mean)
+    plog("DEBUG", "Refreshed sorted list of circuits")
+
   def enqueue_pings(self):
-    """ To be schedule_immediated by pinger before the first connection is triggered """
-    print("\n")
+    """ To be schedule_immediated by pinger before the initial connection is triggered """
+    print("")
     circs = self.circuits.values()
     for c in circs:
       if c.built:
         # Get id of c
       	id = c.circ_id
-        if measure_partial_circs:
+        if self.partial_circs:
 	  # If partial measures wanted: get length
 	  path_len = len(c.path)
 	  for i in xrange(1, path_len):
@@ -705,15 +703,14 @@
     """ Attach a ping stream to its circuit """
     if self.ping_queue.empty():
       # This round has finished
-      plog("INFO", "Queue is empty --> round finished, closing stream " + str(stream.strm_id))
-      self.c.close_stream(stream.strm_id, 5)
-      # Fire the event
-      finished_event.set()
+      plog("INFO", "Queue is empty --> no circuits to test, closing stream " + str(stream.strm_id))
+      self.close_stream(stream.strm_id, 5)
       # Call the rest from here?
       self.print_circuits()
-      if measure_partial_circs:
+      if self.partial_circs:
         self.compute_link_RTTs()
-      return
+      self.enqueue_pings()
+
     else:
       # Get the info and extract
       ping_info = self.ping_queue.get()
@@ -733,14 +730,53 @@
             plog("WARN", "Circuit not built or closed")
 	    self.attach_ping(stream)
         else:
-          # Go to next test if circuit is gone
+          # Go to next test if circuit is gone or we get an ErrorReply
           plog("WARN", "Circuit " + str(circ_id) + " does not exist anymore --> passing")
           self.attach_ping(stream)
       except TorCtl.ErrorReply, e:
-        plog("WARN", "Error attaching stream: " + str(e.args))
+        plog("WARN", "Error attaching stream " + str(stream.strm_id) + " :" + str(e.args))
+	self.attach_ping(stream)
 
+  def record_ping(self, s):
+    """ Record a ping from a stream event (DETACHED or CLOSED) """
+    # No timeout, this is a successful ping: measure here	  
+    hop = self.streams[s.strm_id].hop
+    # Compute RTT using arrived_at 
+    rtt = s.arrived_at - self.start_times[(s.circ_id, hop)]
+    plog("INFO", "Measured RTT: " + str(rtt) + " sec")
+    # Save RTT to circuit
+    self.circuits[s.circ_id].part_rtts[hop] = rtt
+    
+    if hop == None:
+      # This is a total circuit measuring
+      self.circuits[s.circ_id].add_rtt(rtt)
+      plog("DEBUG", "Added RTT to history: " + str(self.circuits[s.circ_id].stats.values))	  
+      
+      # Close if num_tests is reached in testing_mode         
+      if testing_mode:
+        if self.circuits[s.circ_id].age == num_tests:
+          plog("DEBUG", "Closing circ " + str(s.circ_id) + ": num_tests is reached")
+          # Save stats to a file for generating plots etc.
+          if self.partial_circs:
+	    if self.circuits[s.circ_id].rtt_created:
+	      # TODO: Do we have to check if this circuit is _really_ new?
+              self.filehandler.write(str(self.circuits[s.circ_id].stats.mean))
+          else:
+	    self.filehandler.write(str(self.circuits[s.circ_id].stats.mean))
+	  # Close the circuit
+          self.close_circuit(s.circ_id)
+      
+      # Close if slow-max is reached on mean RTT
+      if self.circuits[s.circ_id].stats.mean >= slow:
+        self.circuits[s.circ_id].slowness_counter += 1
+        if self.circuits[s.circ_id].slowness_counter >= slowness_limit and not self.circuits[s.circ_id].closed:
+          plog("DEBUG", "Slow-max (" + str(slowness_limit) + ") is reached --> closing circuit " + str(s.circ_id))
+          self.close_circuit(s.circ_id)
+      # Resort only if this is for the complete circ
+      self.refresh_sorted_list()
+
   def stream_status_event(self, s):
-    """ Separate Pings from regular streams directly """
+    """ Separate pings from regular streams directly """
     if not (s.target_host == ping_dummy_host and s.target_port == ping_dummy_port):
       # This is no ping, call the other method
       return StreamHandler.stream_status_event(self, s)
@@ -763,67 +799,79 @@
       # Measure here, means save arrived_at in the dict
       self.start_times[(s.circ_id, self.streams[s.strm_id].hop)] = s.arrived_at
   
-    # DETACHED (CLOSED + TORPROTOCOL is also ping, some routers send it when measuring 1-hop)
-    elif s.status == "DETACHED" or (s.status == "CLOSED" and s.remote_reason == "TORPROTOCOL"):
+    # DETACHED
+    elif s.status == "DETACHED":      
       if (s.reason == "TIMEOUT"):
         self.circuits[s.circ_id].timeout_counter += 1
-	self.circuits[s.circ_id].slowness_counter += 1
-	plog("DEBUG", str(self.circuits[s.circ_id].timeout_counter) + " timeout(s) on circuit " + str(s.circ_id))
-	if self.circuits[s.circ_id].timeout_counter >= timeout_limit and not self.circuits[s.circ_id].closed:
-	  # Close the circuit
-	  plog("DEBUG", "Reached limit on timeouts --> closing circuit " + str(s.circ_id))
-	  self.circuits[s.circ_id].closed = True
-	  try: self.c.close_circuit(s.circ_id)
-	  except TorCtl.ErrorReply, e: 
-	    plog("ERROR", "Failed closing circuit " + str(s.circ_id) + ": " + str(e))	    
-	# Set RTT for circ to None
-	self.circuits[s.circ_id].current_rtt = None
-      
+        self.circuits[s.circ_id].slowness_counter += 1
+        plog("DEBUG", str(self.circuits[s.circ_id].timeout_counter) + " timeout(s) on circuit " + str(s.circ_id))
+        if self.circuits[s.circ_id].timeout_counter >= timeout_limit and not self.circuits[s.circ_id].closed:
+          # Close the circuit
+          plog("DEBUG", "Reached limit on timeouts --> closing circuit " + str(s.circ_id))
+          self.close_circuit(s.circ_id)
+        # Set RTT for this circ to None
+        self.circuits[s.circ_id].current_rtt = None
       else:
-        # No timeout, this is a successful ping: measure here	  
-        hop = self.streams[s.strm_id].hop
-        # Compute RTT using arrived_at 
-        rtt = s.arrived_at - self.start_times[(s.circ_id, hop)]
-        plog("INFO", "Measured RTT: " + str(rtt) + " sec")
-        # Save RTT to circuit
-        self.circuits[s.circ_id].part_rtts[hop] = rtt
+        # No timeout: Record the result
+        self.record_ping(s)              
+      # Close the stream
+      self.close_stream(s.strm_id, 5)
 
-        if hop == None:
-          # This is a total circuit measuring
-	  self.circuits[s.circ_id].add_rtt(rtt)
-	  plog("DEBUG", "Added RTT to history: " + str(self.circuits[s.circ_id].stats.values))
-	  
-	  # Close if num_tests is reached          
-	  if testing_mode:
-	    if self.circuits[s.circ_id].age >= num_tests:
-	      plog("DEBUG", "Closing circ " + str(s.circ_id) + ": num_tests is reached")
-	      self.circuits[s.circ_id].closed = True
-	      # Save stats to a file in for generating plots etc.
-	      self.filehandler.write(str(self.circuits[s.circ_id].stats.mean) + "\t" + str(self.circuits[s.circ_id].stats.dev))
-	      self.c.close_circuit(s.circ_id)
+    # CLOSED + END is also ping, some routers send it when measuring 1-hop
+    # better measure on FAILED?
+    elif s.status == "CLOSED":
+      if s.reason == "END":
+        # Only record
+        self.record_ping(s)
 
-	  # Close if slow-max is reached on mean RTT
-          if self.circuits[s.circ_id].stats.mean >= slow:
-	    self.circuits[s.circ_id].slowness_counter += 1
-	    if self.circuits[s.circ_id].slowness_counter >= slowness_limit and not self.circuits[s.circ_id].closed:
-	      plog("DEBUG", "Slow-max (" + str(slowness_limit) + ") is reached --> closing circuit " + str(s.circ_id))
-	      self.circuits[s.circ_id].closed = True
-	      self.c.close_circuit(s.circ_id)
+  def get_trad_circs(self):
+    """ Count the circuits with rtt_created == False """
+    trad_circs = 0
+    for c in self.circuits.values():
+      if c.rtt_created == False:
+        trad_circs += 1
+    return trad_circs
 
-          # Resort only if this is for the complete circ
-          self.refresh_sorted_list()
-
-      if s.status == "CLOSED":
-        # Stream is gone .. we have to create a new ping :(
-        t = threading.Thread(None, self.pinger.ping, "Ping")
-	t.setDaemon(True)
-	t.start()
-	return
-
-      # Call attach ping here and use only one stream for all tests
-      self.attach_ping(self.streams[s.strm_id])
-      return
-
+  def build_idle_circuit(self):
+    """ Override from CircuitHandler to support circuit-creation from the NetworkModel """
+    if self.partial_circs:
+      circ = None
+      # This is to ensure expansion of the explored subnet
+      # Check if ratio would be ok when adding new rtt_created circ
+      trad = float(self.get_trad_circs())
+      ratio = trad/(len(self.circuits.values())+1)
+      plog("DEBUG","Expected Ratio = " + str(ratio) + " >= " + str(min_ratio) + " ?")
+      if ratio >= min_ratio:
+        # Get the proposals RTT <= slow
+	proposals = self.model.check_proposals(slow)
+	# Check if we have >= min_proposals
+        if len(proposals) >= min_proposals:
+	  proposals = sort_list(proposals, lambda x: x.rtt)
+	  # Check them out
+	  while len(proposals) >= 1:
+	    # Random choice or choose the fastest!
+	      
+	    choice = random.choice(proposals)
+            #choice = proposals[0]
+	    
+            # Check if we already have a circ with this path
+            if self.check_path(choice.path):
+              plog("INFO", "Chosen proposal: " + choice.to_string())
+              try:
+                circ = self.c.build_circuit_from_path(choice.path)
+                self.circuits[circ.circ_id] = circ
+	        return
+              except TorCtl.ErrorReply, e:
+                plog("NOTICE", "Error building circuit: " + str(e.args))
+            else:
+              # Remove this proposals
+              plog("DEBUG", "Proposed circuit already exists")
+	      proposals.remove(choice)
+    
+    # Build a circuit with the standard method
+    plog("DEBUG", "Falling back to normal path selection")
+    CircuitHandler.build_idle_circuit(self)
+        
 ######################################### BEGIN: Pinger            #####################
 
 class Pinger(threading.Thread):
@@ -834,21 +882,11 @@
   
   def run(self):
     """ The run()-method """
+    time.sleep(initial_interval)
+    self.handler.schedule_immediate(lambda x: x.enqueue_pings())
     while self.isAlive():
+      self.ping()
       time.sleep(sleep_interval)
-      self.do_work()
-
-  def do_work(self):
-    """ Do the work """
-    # Event is only needed, because some routers close our connection if trying 
-    # to use them as one-hop, so we need to create a new connection sometimes and
-    # cannot rely on the failing of our first connection
-    finished_event.clear()
-    # Let all circs to test be enqueued 
-    self.handler.schedule_immediate(lambda x: x.enqueue_pings())
-    # Simply trigger only _one_ connection
-    self.ping()
-    finished_event.wait()
   
   # No "try .. except .. finally .." in Python < 2.5 !
   def ping(self):
@@ -879,18 +917,20 @@
   """ Setup a router object representing this proxy """
   global path_config
   plog("INFO","Setting up our location")
-  ip = 0
+  ip = None
   # Try to get our IP from Tor
   try:
     info = conn.get_info("address")
     ip = info["address"]
   except: 
     plog("ERROR", "Could not get our IP")
+    ip = "127.0.0.1"
   # Set up a router object
   router = GeoIPSupport.GeoIPRouter(TorCtl.Router(None,"ROOT",None,False,None,None,ip,None,None))
-  plog("INFO", "Our IP address is " + router.get_ip_dotted() + " [" + router.country_code + "]")
-  # To be configured
-  path_config.src_country = router.country_code
+  # TODO: Check if ip == None
+  plog("INFO", "Our IP address is " + router.get_ip_dotted() + " [" + str(router.country_code) + "]")
+  # Set entry_country from here?
+  # path_config.entry_country = router.country_code
   return router
  
 def configure(conn):
@@ -914,9 +954,13 @@
   configure(conn)
   # Set Handler to the connection
   if measure_circs:
-    handler = PingHandler(conn, __selmgr, router)
+    if measure_partial_circs:
+      handler = PingHandler(conn, __selmgr, idle_circuits, router, True)
+    else:
+      handler = PingHandler(conn, __selmgr, idle_circuits, router)
   else:
-    handler = StreamHandler(conn, __selmgr)
+    # No pings, only a StreamHandler
+    handler = StreamHandler(conn, __selmgr, idle_circuits)
   conn.set_event_handler(handler)
   # Go to sleep to be able to get killed from the commandline
   try: