def create_circuit(cons_rel_stats, cons_valid_after, cons_fresh_until, cons_bw_weights, cons_bwweightscale, descriptors, hibernating_status, guards, circ_time, circ_fast, circ_stable, circ_internal, circ_ip, circ_port, congmodel, pdelmodel, weighted_exits=None, exits_exact=False, weighted_middles=None, weighted_guards=None, callbacks=None): """Creates path for requested circuit based on the input consensus statuses and descriptors. Uses congestion-aware path selection. Inputs: cons_rel_stats: (dict) relay fingerprint keys and relay status vals cons_valid_after: (int) timestamp of valid_after for consensus cons_fresh_until: (int) timestamp of fresh_until for consensus cons_bw_weights: (dict) bw_weights of consensus cons_bwweightscale: (should be float()able) bwweightscale of consensus descriptors: (dict) relay fingerprint keys and descriptor vals hibernating_status: (dict) indicates hibernating relays guards: (dict) contains guards of requesting client circ_time: (int) timestamp of circuit request circ_fast: (bool) all relays should be fast circ_stable: (bool) all relays should be stable circ_internal: (bool) circuit is for name resolution or hidden service circ_ip: (str) IP address of destination (None if not known) circ_port: (int) desired TCP port (None if not known) congmodel: congestion model pdelmodel: propagation delay model weighted_exits: (list) (middle, cum_weight) pairs for exit position exits_exact: (bool) Is weighted_exits exact or does it need rechecking? weighed_exits is special because exits are chosen first and thus don't depend on the other circuit positions, and so potentially are precomputed exactly. weighted_middles: (list) (middle, cum_weight) pairs for middle position weighted_guards: (list) (middle, cum_weight) pairs for middle position callbacks: object w/ method circuit_creation(circuit) Output: circuit: (dict) a newly created circuit with keys 'time': (int) seconds from time zero 'fast': (bool) relays must have Fast flag 'stable': (bool) relays must have Stable flag 'internal': (bool) is internal (e.g. for hidden service) 'dirty_time': (int) timestamp of time dirtied, None if clean 'path': (tuple) list in-order fingerprints for path's nodes 'covering': (set) ports with needs covered by circuit """ # 'cons_rel_stats': (dict) relay stats for active consensus if (circ_time < cons_valid_after) or\ (circ_time >= cons_fresh_until): raise ValueError('consensus not fresh for circ_time in create_circuit') # choose num_paths_choose paths, and choose one with best predicted latency best_latency = None best_path = None for k in xrange(num_paths_choose): print('Choosing path #{0} to predict latency'.format(k)) num_attempts = 0 ntor_supported = False while (num_attempts < pathsim.TorOptions.max_populate_attempts) and\ (not ntor_supported): # select exit node i = 1 while (True): exit_node = pathsim.select_exit_node(cons_bw_weights, cons_bwweightscale, cons_rel_stats, descriptors, circ_fast, circ_stable, circ_internal, circ_ip, circ_port, weighted_exits, exits_exact) # exit_node = select_weighted_node(weighted_exits) if (not hibernating_status[exit_node]): break if _testing: print('Exit selection #{0} is hibernating - retrying.'.\ format(i)) i += 1 if _testing: print('Exit node: {0} [{1}]'.format( cons_rel_stats[exit_node].nickname, cons_rel_stats[exit_node].fingerprint)) # select guard node # Hibernation status again checked here to reflect how in Tor # new guards would be chosen and added to the list prior to a circuit- # creation attempt. If the circuit fails at a new guard, that guard # gets removed from the list. while True: # get first <= num_guards guards suitable for circuit circ_guards = pathsim.get_guards_for_circ(cons_bw_weights,\ cons_bwweightscale, cons_rel_stats, descriptors,\ circ_fast, circ_stable, guards,\ exit_node, circ_time, weighted_guards) guard_node = choice(circ_guards) if (hibernating_status[guard_node]): if (not guards[guard_node]['made_contact']): if _testing: print(\ '[Time {0}]: Removing new hibernating guard: {1}.'.\ format(circ_time, cons_rel_stats[guard_node].nickname)) del guards[guard_node] elif (guards[guard_node]['unreachable_since'] != None): if _testing: print(\ '[Time {0}]: Guard retried but hibernating: {1}'.\ format(circ_time, cons_rel_stats[guard_node].nickname)) guards[guard_node]['last_attempted'] = circ_time else: if _testing: print('[Time {0}]: Guard newly hibernating: {1}'.\ format(circ_time, \ cons_rel_stats[guard_node].nickname)) guards[guard_node]['unreachable_since'] = circ_time guards[guard_node]['last_attempted'] = circ_time else: guards[guard_node]['unreachable_since'] = None guards[guard_node]['made_contact'] = True break if _testing: print('Guard node: {0} [{1}]'.format( cons_rel_stats[guard_node].nickname, cons_rel_stats[guard_node].fingerprint)) # select middle node # As with exit selection, hibernating status checked here to mirror Tor # selecting middle, having the circuit fail, reselecting a path, # and attempting circuit creation again. i = 1 while (True): middle_node = pathsim.select_middle_node(cons_bw_weights,\ cons_bwweightscale, cons_rel_stats, descriptors, circ_fast,\ circ_stable, exit_node, guard_node, weighted_middles) if (not hibernating_status[middle_node]): break if _testing: print(\ 'Middle selection #{0} is hibernating - retrying.'.format(i)) i += 1 if _testing: print('Middle node: {0} [{1}]'.format( cons_rel_stats[middle_node].nickname, cons_rel_stats[middle_node].fingerprint)) # ensure one member of the circuit supports the ntor handshake ntor_supported = pathsim.circuit_supports_ntor(guard_node, middle_node, exit_node, descriptors) num_attempts += 1 if pathsim._testing: if ntor_supported: print('Chose ntor-compatible circuit in {} tries'.\ format(num_attempts)) if (not ntor_supported): raise ValueError('ntor-compatible circuit not found in {} tries'.\ format(num_attempts)) latency = #SAFEST TODO: get latency for circuit from SAFEST VCS service if (best_latency == None) or (latency < best_latency): best_latency = latency best_circ = (guard_node, middle_node, exit_node) circuit = {'time':circ_time, 'fast':circ_fast, 'stable':circ_stable, 'internal':circ_internal, 'dirty_time':None, 'path':best_circ, 'covering':set(), 'avg_ping':None} # execute callback to allow logging on circuit creation if (callbacks is not None): callbacks.circuit_creation(circuit) return circuit
def create_circuit(cons_rel_stats, cons_valid_after, cons_fresh_until, cons_bw_weights, cons_bwweightscale, descriptors, hibernating_status, guards, circ_time, circ_fast, circ_stable, circ_internal, circ_ip, circ_port, congmodel, pdelmodel, weighted_exits=None, exits_exact=False, weighted_middles=None, weighted_guards=None, callbacks=None): """Creates path for requested circuit based on the input consensus statuses and descriptors. Uses congestion-aware path selection. Inputs: cons_rel_stats: (dict) relay fingerprint keys and relay status vals cons_valid_after: (int) timestamp of valid_after for consensus cons_fresh_until: (int) timestamp of fresh_until for consensus cons_bw_weights: (dict) bw_weights of consensus cons_bwweightscale: (should be float()able) bwweightscale of consensus descriptors: (dict) relay fingerprint keys and descriptor vals hibernating_status: (dict) indicates hibernating relays guards: (dict) contains guards of requesting client circ_time: (int) timestamp of circuit request circ_fast: (bool) all relays should be fast circ_stable: (bool) all relays should be stable circ_internal: (bool) circuit is for name resolution or hidden service circ_ip: (str) IP address of destination (None if not known) circ_port: (int) desired TCP port (None if not known) congmodel: congestion model pdelmodel: propagation delay model weighted_exits: (list) (middle, cum_weight) pairs for exit position exits_exact: (bool) Is weighted_exits exact or does it need rechecking? weighed_exits is special because exits are chosen first and thus don't depend on the other circuit positions, and so potentially are precomputed exactly. weighted_middles: (list) (middle, cum_weight) pairs for middle position weighted_guards: (list) (middle, cum_weight) pairs for middle position callbacks: object w/ method circuit_creation(circuit) Output: circuit: (dict) a newly created circuit with keys 'time': (int) seconds from time zero 'fast': (bool) relays must have Fast flag 'stable': (bool) relays must have Stable flag 'internal': (bool) is internal (e.g. for hidden service) 'dirty_time': (int) timestamp of time dirtied, None if clean 'path': (tuple) list in-order fingerprints for path's nodes 'covering': (set) ports with needs covered by circuit 'avg_ping': (float) average ping time during most-recent use """ # 'cons_rel_stats': (dict) relay stats for active consensus if (circ_time < cons_valid_after) or\ (circ_time >= cons_fresh_until): raise ValueError('consensus not fresh for circ_time in create_circuit') num_attempts = 0 ntor_supported = False while (num_attempts < pathsim.TorOptions.max_populate_attempts) and\ (not ntor_supported): # select exit node i = 1 while (True): exit_node = pathsim.select_exit_node( cons_bw_weights, cons_bwweightscale, cons_rel_stats, descriptors, circ_fast, circ_stable, circ_internal, circ_ip, circ_port, weighted_exits, exits_exact) # exit_node = pathsim.select_weighted_node(weighted_exits) if (not hibernating_status[exit_node]): break if pathsim._testing: print('Exit selection #{0} is hibernating - retrying.'.\ format(i)) i += 1 if pathsim._testing: print('Exit node: {0} [{1}]'.format( cons_rel_stats[exit_node].nickname, cons_rel_stats[exit_node].fingerprint)) # select guard node # Hibernation status again checked here to reflect how in Tor # new guards would be chosen and added to the list prior to a circuit- # creation attempt. If the circuit fails at a new guard, that guard # gets removed from the list. while True: # get first <= num_guards guards suitable for circuit circ_guards = pathsim.get_guards_for_circ(cons_bw_weights,\ cons_bwweightscale, cons_rel_stats, descriptors,\ circ_fast, circ_stable, guards,\ exit_node, circ_time, weighted_guards) guard_node = choice(circ_guards) if (hibernating_status[guard_node]): if (not guards[guard_node]['made_contact']): if pathsim._testing: print(\ '[Time {0}]: Removing new hibernating guard: {1}.'.\ format(circ_time, cons_rel_stats[guard_node].nickname)) del guards[guard_node] elif (guards[guard_node]['unreachable_since'] != None): if pathsim._testing: print(\ '[Time {0}]: Guard retried but hibernating: {1}'.\ format(circ_time, cons_rel_stats[guard_node].nickname)) guards[guard_node]['last_attempted'] = circ_time else: if pathsim._testing: print('[Time {0}]: Guard newly hibernating: {1}'.\ format(circ_time, \ cons_rel_stats[guard_node].nickname)) guards[guard_node]['unreachable_since'] = circ_time guards[guard_node]['last_attempted'] = circ_time else: guards[guard_node]['unreachable_since'] = None guards[guard_node]['made_contact'] = True break if pathsim._testing: print('Guard node: {0} [{1}]'.format( cons_rel_stats[guard_node].nickname, cons_rel_stats[guard_node].fingerprint)) # select middle node # As with exit selection, hibernating status checked here to mirror Tor # selecting middle, having the circuit fail, reselecting a path, # and attempting circuit creation again. i = 1 while (True): middle_node = pathsim.select_middle_node(cons_bw_weights,\ cons_bwweightscale, cons_rel_stats, descriptors, circ_fast,\ circ_stable, exit_node, guard_node, weighted_middles) if (not hibernating_status[middle_node]): break if pathsim._testing: print(\ 'Middle selection #{0} is hibernating - retrying.'.format(i)) i += 1 if pathsim._testing: print('Middle node: {0} [{1}]'.format( cons_rel_stats[middle_node].nickname, cons_rel_stats[middle_node].fingerprint)) # ensure one member of the circuit supports the ntor handshake ntor_supported = pathsim.circuit_supports_ntor(guard_node, middle_node, exit_node, descriptors) num_attempts += 1 if pathsim._testing: if ntor_supported: print('Chose ntor-compatible circuit in {} tries'.\ format(num_attempts)) if (not ntor_supported): raise ValueError('ntor-compatible circuit not found in {} tries'.\ format(num_attempts)) cum_ping_time = 0 if pathsim._testing: print 'Doing {0} circuit pings on creation... '.format( num_pings_create), for i in xrange(num_pings_create): cum_ping_time += ping_circuit(client_ip, guard_node, middle_node,\ exit_node, cons_rel_stats, descriptors, congmodel, pdelmodel) avg_ping_time = float(cum_ping_time) / num_pings_create if pathsim._testing: print "ave congestion is {0}".format(avg_ping_time) circuit = { 'time': circ_time, 'fast': circ_fast, 'stable': circ_stable, 'internal': circ_internal, 'dirty_time': None, 'path': (guard_node, middle_node, exit_node), 'covering': set(), 'initial_avg_ping': avg_ping_time, 'avg_ping': None } # execute callback to allow logging on circuit creation if (callbacks is not None): callbacks.circuit_creation(circuit) return circuit