Refactoring work for free-threaded transport I/O. Added ingress control bypass on pending path requests.

This commit is contained in:
Mark Qvist
2026-04-15 18:48:17 +02:00
parent 3c50f4aee9
commit c3f0d98e41
4 changed files with 165 additions and 156 deletions
+12 -22
View File
@@ -295,33 +295,26 @@ class Destination:
app_data = returned_app_data app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
if app_data != None: if app_data != None: signed_data += app_data
signed_data += app_data
signature = self.identity.sign(signed_data) signature = self.identity.sign(signed_data)
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+ratchet+signature announce_data = self.identity.get_public_key()+self.name_hash+random_hash+ratchet+signature
if app_data != None: if app_data != None: announce_data += app_data
announce_data += app_data
self.path_responses[tag] = [time.time(), announce_data] self.path_responses[tag] = [time.time(), announce_data]
if path_response: if path_response: announce_context = RNS.Packet.PATH_RESPONSE
announce_context = RNS.Packet.PATH_RESPONSE else: announce_context = RNS.Packet.NONE
else:
announce_context = RNS.Packet.NONE
if ratchet: if ratchet: context_flag = RNS.Packet.FLAG_SET
context_flag = RNS.Packet.FLAG_SET else: context_flag = RNS.Packet.FLAG_UNSET
else:
context_flag = RNS.Packet.FLAG_UNSET
announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context,
attached_interface = attached_interface, context_flag=context_flag) attached_interface = attached_interface, context_flag=context_flag)
if send:
announce_packet.send() if send: announce_packet.send()
else: else: return announce_packet
return announce_packet
def accepts_links(self, accepts = None): def accepts_links(self, accepts = None):
""" """
@@ -330,13 +323,10 @@ class Destination:
:param accepts: If ``True`` or ``False``, this method sets whether the destination accepts incoming link requests. If not provided or ``None``, the method returns whether the destination currently accepts link requests. :param accepts: If ``True`` or ``False``, this method sets whether the destination accepts incoming link requests. If not provided or ``None``, the method returns whether the destination currently accepts link requests.
:returns: ``True`` or ``False`` depending on whether the destination accepts incoming link requests, if the *accepts* parameter is not provided or ``None``. :returns: ``True`` or ``False`` depending on whether the destination accepts incoming link requests, if the *accepts* parameter is not provided or ``None``.
""" """
if accepts == None: if accepts == None: return self.accept_link_requests
return self.accept_link_requests
if accepts: if accepts: self.accept_link_requests = True
self.accept_link_requests = True else: self.accept_link_requests = False
else:
self.accept_link_requests = False
def set_link_established_callback(self, callback): def set_link_established_callback(self, callback):
""" """
+10 -9
View File
@@ -55,8 +55,8 @@ class Interface:
# How many samples to use for announce # How many samples to use for announce
# frequency calculations # frequency calculations
IA_FREQ_SAMPLES = 12 IA_FREQ_SAMPLES = 128
OA_FREQ_SAMPLES = 12 OA_FREQ_SAMPLES = 128
# Maximum amount of ingress limited announces # Maximum amount of ingress limited announces
# to hold at any given time. # to hold at any given time.
@@ -66,12 +66,12 @@ class Interface:
# considered to be newly created. Two # considered to be newly created. Two
# hours by default. # hours by default.
IC_NEW_TIME = 2*60*60 IC_NEW_TIME = 2*60*60
IC_BURST_FREQ_NEW = 3.5 IC_BURST_FREQ_NEW = 6
IC_BURST_FREQ = 12 IC_BURST_FREQ = 35
IC_BURST_HOLD = 1*60 IC_BURST_HOLD = 1*60
IC_BURST_PENALTY = 5*60 IC_BURST_PENALTY = 15
IC_HELD_RELEASE_INTERVAL = 30 IC_HELD_RELEASE_INTERVAL = 2
IC_DEQUE_MIN_SAMPLE = 8 IC_DEQUE_MIN_SAMPLE = 32
AUTOCONFIGURE_MTU = False AUTOCONFIGURE_MTU = False
FIXED_MTU = False FIXED_MTU = False
@@ -123,13 +123,14 @@ class Interface:
if self.ic_burst_active: if self.ic_burst_active:
if ia_freq < freq_threshold and time.time() > self.ic_burst_activated+self.ic_burst_hold: if ia_freq < freq_threshold and time.time() > self.ic_burst_activated+self.ic_burst_hold:
self.ic_burst_active = False self.ic_burst_active = False
self.ic_held_release = time.time() + self.ic_burst_penalty
return True return True
else: else:
if ia_freq > freq_threshold: if ia_freq > freq_threshold:
self.ic_burst_active = True self.ic_burst_active = True
self.ic_burst_activated = time.time() self.ic_burst_activated = time.time()
self.ic_held_release = time.time() + self.ic_burst_penalty
return True return True
else: return False else: return False
@@ -174,7 +175,7 @@ class Interface:
def process_held_announces(self): def process_held_announces(self):
try: try:
if not self.should_ingress_limit() and len(self.held_announces) > 0 and time.time() > self.ic_held_release: if len(self.held_announces) > 0 and time.time() > self.ic_held_release:
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
ia_freq = self.incoming_announce_frequency() ia_freq = self.incoming_announce_frequency()
if ia_freq < freq_threshold: if ia_freq < freq_threshold:
+4 -8
View File
@@ -722,12 +722,9 @@ class Link:
pass pass
def link_closed(self): def link_closed(self):
for resource in self.incoming_resources: for resource in self.incoming_resources: resource.cancel()
resource.cancel() for resource in self.outgoing_resources: resource.cancel()
for resource in self.outgoing_resources: if self._channel: self._channel._shutdown()
resource.cancel()
if self._channel:
self._channel._shutdown()
self.prv = None self.prv = None
self.pub = None self.pub = None
@@ -741,8 +738,7 @@ class Link:
self.destination.links.remove(self) self.destination.links.remove(self)
if self.callbacks.link_closed != None: if self.callbacks.link_closed != None:
try: try: self.callbacks.link_closed(self)
self.callbacks.link_closed(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing link closed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Error while executing link closed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
+137 -115
View File
@@ -360,7 +360,7 @@ class Transport:
if len(tunnel_paths) > 0: if len(tunnel_paths) > 0:
tunnel = [tunnel_id, None, tunnel_paths, expires] tunnel = [tunnel_id, None, tunnel_paths, expires]
Transport.tunnels[tunnel_id] = tunnel with Transport.tunnels_lock: Transport.tunnels[tunnel_id] = tunnel
if len(Transport.path_table) == 1: specifier = "entry" if len(Transport.path_table) == 1: specifier = "entry"
else: specifier = "entries" else: specifier = "entries"
@@ -908,6 +908,7 @@ class Transport:
except Exception as e: except Exception as e:
RNS.log("An exception occurred while running Transport jobs.", RNS.LOG_ERROR) RNS.log("An exception occurred while running Transport jobs.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.trace_exception(e) # TODO: Remove
for packet in outgoing: packet.send() for packet in outgoing: packet.send()
@@ -1553,12 +1554,17 @@ class Transport:
# potential ingress limiting. Already known # potential ingress limiting. Already known
# destinations will have re-announces controlled # destinations will have re-announces controlled
# by normal announce rate limiting. # by normal announce rate limiting.
if interface.should_ingress_limit(): if packet.destination_hash in Transport.path_requests or packet.destination_hash in Transport.discovery_path_requests:
# RNS.log(f"Skipping ingress limit check for {RNS.prettyhexrep(packet.destination_hash)} due to waiting path requests", RNS.LOG_DEBUG)
pass
elif interface.should_ingress_limit():
interface.hold_announce(packet) interface.hold_announce(packet)
return return
with Transport.destinations_lock: with Transport.destinations_lock:
local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None) local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None)
if local_destination == None and RNS.Identity.validate_announce(packet): if local_destination == None and RNS.Identity.validate_announce(packet):
if packet.transport_id != None: if packet.transport_id != None:
received_from = packet.transport_id received_from = packet.transport_id
@@ -1575,14 +1581,15 @@ class Transport:
if announce_entry[IDX_AT_RETRIES] > 0: if announce_entry[IDX_AT_RETRIES] > 0:
if announce_entry[IDX_AT_LCL_RBRD] >= Transport.LOCAL_REBROADCASTS_MAX: if announce_entry[IDX_AT_LCL_RBRD] >= Transport.LOCAL_REBROADCASTS_MAX:
RNS.log("Completed announce processing for "+RNS.prettyhexrep(packet.destination_hash)+", local rebroadcast limit reached", RNS.LOG_EXTREME) RNS.log("Completed announce processing for "+RNS.prettyhexrep(packet.destination_hash)+", local rebroadcast limit reached", RNS.LOG_EXTREME)
if packet.destination_hash in Transport.announce_table: Transport.announce_table.pop(packet.destination_hash) with Transport.announce_table_lock:
if packet.destination_hash in Transport.announce_table: Transport.announce_table.pop(packet.destination_hash)
if packet.hops-1 == announce_entry[IDX_AT_HOPS]+1 and announce_entry[IDX_AT_RETRIES] > 0: if packet.hops-1 == announce_entry[IDX_AT_HOPS]+1 and announce_entry[IDX_AT_RETRIES] > 0:
now = time.time() now = time.time()
if now < announce_entry[IDX_AT_RTRNS_TMO]: if now < announce_entry[IDX_AT_RTRNS_TMO]:
RNS.log("Rebroadcasted announce for "+RNS.prettyhexrep(packet.destination_hash)+" has been passed on to another node, no further tries needed", RNS.LOG_EXTREME) RNS.log("Rebroadcasted announce for "+RNS.prettyhexrep(packet.destination_hash)+" has been passed on to another node, no further tries needed", RNS.LOG_EXTREME)
if packet.destination_hash in Transport.announce_table: with Transport.announce_table_lock:
Transport.announce_table.pop(packet.destination_hash) if packet.destination_hash in Transport.announce_table: Transport.announce_table.pop(packet.destination_hash)
else: else:
received_from = packet.destination_hash received_from = packet.destination_hash
@@ -1706,9 +1713,7 @@ class Transport:
else: else:
rate_entry["last"] = now rate_entry["last"] = now
else: else: rate_blocked = True
rate_blocked = True
retries = 0 retries = 0
announce_hops = packet.hops announce_hops = packet.hops
@@ -1961,29 +1966,31 @@ class Transport:
# Handling for local data packets # Handling for local data packets
elif packet.packet_type == RNS.Packet.DATA: elif packet.packet_type == RNS.Packet.DATA:
if packet.destination_type == RNS.Destination.LINK: if packet.destination_type == RNS.Destination.LINK:
for link in Transport.active_links: with Transport.active_links_lock:
if link.link_id == packet.destination_hash: for link in Transport.active_links:
if link.attached_interface == packet.receiving_interface: if link.link_id == packet.destination_hash:
packet.link = link if link.attached_interface == packet.receiving_interface:
if packet.context == RNS.Packet.CACHE_REQUEST: packet.link = link
cached_packet = Transport.get_cached_packet(packet.data) if packet.context == RNS.Packet.CACHE_REQUEST:
if cached_packet != None: cached_packet = Transport.get_cached_packet(packet.data)
cached_packet.unpack() if cached_packet != None:
RNS.Packet(destination=link, data=cached_packet.data, cached_packet.unpack()
packet_type=cached_packet.packet_type, context=cached_packet.context).send() RNS.Packet(destination=link, data=cached_packet.data,
packet_type=cached_packet.packet_type, context=cached_packet.context).send()
else: link.receive(packet) else: link.receive(packet)
break
else: else:
# In the strange and rare case that an interface # In the strange and rare case that an interface
# is partly malfunctioning, and a link-associated # is partly malfunctioning, and a link-associated
# packet is being received on an interface that # packet is being received on an interface that
# has failed sending, and transport has failed over # has failed sending, and transport has failed over
# to another path, we remove this packet hash from # to another path, we remove this packet hash from
# the filter hashlist so the link can receive the # the filter hashlist so the link can receive the
# packet when it finally arrives over another path. # packet when it finally arrives over another path.
while packet.packet_hash in Transport.packet_hashlist: while packet.packet_hash in Transport.packet_hashlist:
Transport.packet_hashlist.remove(packet.packet_hash) Transport.packet_hashlist.remove(packet.packet_hash)
else: else:
for destination in Transport.destinations: for destination in Transport.destinations:
if destination.hash == packet.destination_hash and destination.type == packet.destination_type: if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
@@ -1997,6 +2004,7 @@ class Transport:
if destination.callbacks.proof_requested(packet): packet.prove() if destination.callbacks.proof_requested(packet): packet.prove()
except Exception as e: except Exception as e:
RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
break
# Handling for proofs and link-request proofs # Handling for proofs and link-request proofs
elif packet.packet_type == RNS.Packet.PROOF: elif packet.packet_type == RNS.Packet.PROOF:
@@ -2038,48 +2046,57 @@ class Transport:
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
else: else:
RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG) RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG)
else: else:
# Check if we can deliver it to a local # Check if we can deliver it to a local
# pending link # pending link
for link in Transport.pending_links:
if link.link_id == packet.destination_hash:
# We need to also allow an expected hops value of
# PATHFINDER_M, since in some cases, the number of hops
# to the destination will be unknown at link creation
# time. The real chance of this occuring is likely to be
# extremely small, and this allowance could probably
# be discarded without major issues, but it is kept
# for now to ensure backwards compatibility.
# TODO: Probably reset check back to pending_link = None
# if packet.hops == link.expected_hops: with Transport.pending_links_lock:
# within one of the next releases for link in Transport.pending_links:
if link.link_id == packet.destination_hash:
# We need to also allow an expected hops value of
# PATHFINDER_M, since in some cases, the number of hops
# to the destination will be unknown at link creation
# time. The real chance of this occuring is likely to be
# extremely small, and this allowance could probably
# be discarded without major issues, but it is kept
# for now to ensure backwards compatibility.
if packet.hops == link.expected_hops or link.expected_hops == RNS.Transport.PATHFINDER_M: # TODO: Probably reset check back to
# Add this packet to the filter hashlist if we # if packet.hops == link.expected_hops:
# have determined that it's actually destined # within one of the next releases
# for this system, and then validate the proof
Transport.add_packet_hash(packet.packet_hash) if packet.hops == link.expected_hops or link.expected_hops == RNS.Transport.PATHFINDER_M:
link.validate_proof(packet) # Add this packet to the filter hashlist if we
# have determined that it's actually destined
# for this system, and then validate the proof
Transport.add_packet_hash(packet.packet_hash)
pending_link = link
break
if pending_link: pending_link.validate_proof(packet)
elif packet.context == RNS.Packet.RESOURCE_PRF: elif packet.context == RNS.Packet.RESOURCE_PRF:
for link in Transport.active_links: with Transport.active_links_lock:
if link.link_id == packet.destination_hash:
link.receive(packet)
else:
if packet.destination_type == RNS.Destination.LINK:
for link in Transport.active_links: for link in Transport.active_links:
if link.link_id == packet.destination_hash: if link.link_id == packet.destination_hash:
packet.link = link link.receive(packet)
break
else:
if packet.destination_type == RNS.Destination.LINK:
with Transport.active_links_lock:
for link in Transport.active_links:
if link.link_id == packet.destination_hash:
packet.link = link
break
if len(packet.data) == RNS.PacketReceipt.EXPL_LENGTH: if len(packet.data) == RNS.PacketReceipt.EXPL_LENGTH: proof_hash = packet.data[:RNS.Identity.HASHLENGTH//8]
proof_hash = packet.data[:RNS.Identity.HASHLENGTH//8] else: proof_hash = None
else:
proof_hash = None
# Check if this proof needs to be transported # Check if this proof needs to be transported
if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table: if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table:
reverse_entry = Transport.reverse_table.pop(packet.destination_hash) with Transport.reverse_table_lock: reverse_entry = Transport.reverse_table.pop(packet.destination_hash)
if packet.receiving_interface == reverse_entry[IDX_RT_OUTB_IF]: if packet.receiving_interface == reverse_entry[IDX_RT_OUTB_IF]:
RNS.log("Proof received on correct interface, transporting it via "+str(reverse_entry[IDX_RT_RCVD_IF]), RNS.LOG_EXTREME) RNS.log("Proof received on correct interface, transporting it via "+str(reverse_entry[IDX_RT_RCVD_IF]), RNS.LOG_EXTREME)
new_raw = packet.raw[0:1] new_raw = packet.raw[0:1]
@@ -2089,20 +2106,21 @@ class Transport:
else: else:
RNS.log("Proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) RNS.log("Proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
for receipt in Transport.receipts: with Transport.receipts_lock:
receipt_validated = False for receipt in Transport.receipts:
if proof_hash != None: receipt_validated = False
# Only test validation if hash matches if proof_hash != None:
if receipt.hash == proof_hash: # Only test validation if hash matches
if receipt.hash == proof_hash:
receipt_validated = receipt.validate_proof_packet(packet)
else:
# In case of an implicit proof, we have
# to check every single outstanding receipt
receipt_validated = receipt.validate_proof_packet(packet) receipt_validated = receipt.validate_proof_packet(packet)
else:
# In case of an implicit proof, we have
# to check every single outstanding receipt
receipt_validated = receipt.validate_proof_packet(packet)
if receipt_validated: if receipt_validated:
if receipt in Transport.receipts: if receipt in Transport.receipts:
Transport.receipts.remove(receipt) Transport.receipts.remove(receipt)
@staticmethod @staticmethod
def synthesize_tunnel(interface): def synthesize_tunnel(interface):
@@ -2151,9 +2169,10 @@ class Transport:
@staticmethod @staticmethod
def void_tunnel_interface(tunnel_id): def void_tunnel_interface(tunnel_id):
if tunnel_id in Transport.tunnels: with Transport.tunnels_lock:
RNS.log(f"Voiding tunnel interface {Transport.tunnels[tunnel_id][IDX_TT_IF]}", RNS.LOG_EXTREME) if tunnel_id in Transport.tunnels:
Transport.tunnels[tunnel_id][IDX_TT_IF] = None RNS.log(f"Voiding tunnel interface {Transport.tunnels[tunnel_id][IDX_TT_IF]}", RNS.LOG_EXTREME)
Transport.tunnels[tunnel_id][IDX_TT_IF] = None
@staticmethod @staticmethod
def handle_tunnel(tunnel_id, interface): def handle_tunnel(tunnel_id, interface):
@@ -2161,9 +2180,10 @@ class Transport:
if not tunnel_id in Transport.tunnels: if not tunnel_id in Transport.tunnels:
RNS.log("Tunnel endpoint "+RNS.prettyhexrep(tunnel_id)+" established.", RNS.LOG_DEBUG) RNS.log("Tunnel endpoint "+RNS.prettyhexrep(tunnel_id)+" established.", RNS.LOG_DEBUG)
paths = {} paths = {}
tunnel_entry = [tunnel_id, interface, paths, expires] with Transport.tunnels_lock:
interface.tunnel_id = tunnel_id tunnel_entry = [tunnel_id, interface, paths, expires]
Transport.tunnels[tunnel_id] = tunnel_entry interface.tunnel_id = tunnel_id
Transport.tunnels[tunnel_id] = tunnel_entry
else: else:
RNS.log("Tunnel endpoint "+RNS.prettyhexrep(tunnel_id)+" reappeared. Restoring paths...", RNS.LOG_DEBUG) RNS.log("Tunnel endpoint "+RNS.prettyhexrep(tunnel_id)+" reappeared. Restoring paths...", RNS.LOG_DEBUG)
tunnel_entry = Transport.tunnels[tunnel_id] tunnel_entry = Transport.tunnels[tunnel_id]
@@ -2173,36 +2193,38 @@ class Transport:
paths = tunnel_entry[IDX_TT_PATHS] paths = tunnel_entry[IDX_TT_PATHS]
deprecated_paths = [] deprecated_paths = []
for destination_hash, path_entry in paths.items(): with Transport.tunnels_lock:
received_from = path_entry[1] for destination_hash, path_entry in paths.items():
announce_hops = path_entry[2] received_from = path_entry[1]
expires = path_entry[3] announce_hops = path_entry[2]
random_blobs = list(set(path_entry[4])) expires = path_entry[3]
receiving_interface = interface random_blobs = list(set(path_entry[4]))
packet_hash = path_entry[6] receiving_interface = interface
new_entry = [time.time(), received_from, announce_hops, expires, random_blobs, receiving_interface, packet_hash] packet_hash = path_entry[6]
new_entry = [time.time(), received_from, announce_hops, expires, random_blobs, receiving_interface, packet_hash]
should_add = False should_add = False
if destination_hash in Transport.path_table: with Transport.path_table_lock:
old_entry = Transport.path_table[destination_hash] if destination_hash in Transport.path_table:
old_hops = old_entry[IDX_PT_HOPS] old_entry = Transport.path_table[destination_hash]
old_expires = old_entry[IDX_PT_EXPIRES] old_hops = old_entry[IDX_PT_HOPS]
if announce_hops <= old_hops or time.time() > old_expires: should_add = True old_expires = old_entry[IDX_PT_EXPIRES]
else: RNS.log("Did not restore path to "+RNS.prettyhexrep(destination_hash)+" because a newer path with fewer hops exist", RNS.LOG_DEBUG) if announce_hops <= old_hops or time.time() > old_expires: should_add = True
else: RNS.log("Did not restore path to "+RNS.prettyhexrep(destination_hash)+" because a newer path with fewer hops exist", RNS.LOG_DEBUG)
else: else:
if time.time() < expires: should_add = True if time.time() < expires: should_add = True
else: RNS.log("Did not restore path to "+RNS.prettyhexrep(destination_hash)+" because it has expired", RNS.LOG_DEBUG) else: RNS.log("Did not restore path to "+RNS.prettyhexrep(destination_hash)+" because it has expired", RNS.LOG_DEBUG)
if should_add: if should_add:
Transport.path_table[destination_hash] = new_entry with Transport.path_table_lock: Transport.path_table[destination_hash] = new_entry
RNS.log("Restored path to "+RNS.prettyhexrep(destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(receiving_interface), RNS.LOG_DEBUG) RNS.log("Restored path to "+RNS.prettyhexrep(destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(receiving_interface), RNS.LOG_DEBUG)
else:
deprecated_paths.append(destination_hash) else: deprecated_paths.append(destination_hash)
for deprecated_path in deprecated_paths: for deprecated_path in deprecated_paths:
RNS.log("Removing path to "+RNS.prettyhexrep(deprecated_path)+" from tunnel "+RNS.prettyhexrep(tunnel_id), RNS.LOG_DEBUG) RNS.log("Removing path to "+RNS.prettyhexrep(deprecated_path)+" from tunnel "+RNS.prettyhexrep(tunnel_id), RNS.LOG_DEBUG)
paths.pop(deprecated_path) with Transport.tunnels_lock: paths.pop(deprecated_path)
@staticmethod @staticmethod
def register_destination(destination): def register_destination(destination):
@@ -2302,8 +2324,8 @@ class Transport:
def clean_announce_cache(): def clean_announce_cache():
st = time.time() st = time.time()
target_path = os.path.join(RNS.Reticulum.cachepath, "announces") target_path = os.path.join(RNS.Reticulum.cachepath, "announces")
active_paths = [Transport.path_table[dst_hash][6] for dst_hash in Transport.path_table] with Transport.path_table_lock: active_paths = [Transport.path_table[dst_hash][6] for dst_hash in Transport.path_table]
tunnel_paths = list(set([path_dict[dst_hash][6] for path_dict in [Transport.tunnels[tunnel_id][2] for tunnel_id in Transport.tunnels] for dst_hash in path_dict])) with Transport.tunnels_lock: tunnel_paths = list(set([path_dict[dst_hash][6] for path_dict in [Transport.tunnels[tunnel_id][2] for tunnel_id in Transport.tunnels] for dst_hash in path_dict]))
removed = 0 removed = 0
for packet_hash in os.listdir(target_path): for packet_hash in os.listdir(target_path):
remove = False remove = False
@@ -2314,8 +2336,7 @@ class Transport:
if (not target_hash in active_paths) and (not target_hash in tunnel_paths): remove = True if (not target_hash in active_paths) and (not target_hash in tunnel_paths): remove = True
if remove: os.unlink(full_path); removed += 1 if remove: os.unlink(full_path); removed += 1
if removed > 0: if removed > 0: RNS.log(f"Removed {removed} cached announces in {RNS.prettytime(time.time()-st)}", RNS.LOG_DEBUG)
RNS.log(f"Removed {removed} cached announces in {RNS.prettytime(time.time()-st)}", RNS.LOG_DEBUG)
# When caching packets to storage, they are written # When caching packets to storage, they are written
# exactly as they arrived over their interface. This # exactly as they arrived over their interface. This
@@ -2447,8 +2468,8 @@ class Transport:
if next_hop_interface != None: if next_hop_interface != None:
if next_hop_interface.AUTOCONFIGURE_MTU or next_hop_interface.FIXED_MTU: return next_hop_interface.HW_MTU if next_hop_interface.AUTOCONFIGURE_MTU or next_hop_interface.FIXED_MTU: return next_hop_interface.HW_MTU
else: return None else: return None
else:
return None else: return None
@staticmethod @staticmethod
def next_hop_per_bit_latency(destination_hash): def next_hop_per_bit_latency(destination_hash):
@@ -2511,7 +2532,8 @@ class Transport:
def path_is_unresponsive(destination_hash): def path_is_unresponsive(destination_hash):
with Transport.path_states_lock: with Transport.path_states_lock:
if destination_hash in Transport.path_states: if destination_hash in Transport.path_states:
if Transport.path_states[destination_hash] == Transport.STATE_UNRESPONSIVE: return True if Transport.path_states[destination_hash] == Transport.STATE_UNRESPONSIVE:
return True
return False return False
@@ -2757,7 +2779,8 @@ class Transport:
held_entry = Transport.announce_table[packet.destination_hash] held_entry = Transport.announce_table[packet.destination_hash]
Transport.held_announces[packet.destination_hash] = held_entry Transport.held_announces[packet.destination_hash] = held_entry
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface] with Transport.announce_table_lock:
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
elif is_from_local_client: elif is_from_local_client:
# Forward path request on all interfaces # Forward path request on all interfaces
@@ -2776,7 +2799,7 @@ class Transport:
# except the requestor interface # except the requestor interface
RNS.log("Attempting to discover unknown path to "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG) RNS.log("Attempting to discover unknown path to "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG)
pr_entry = { "destination_hash": destination_hash, "timeout": time.time()+Transport.PATH_REQUEST_TIMEOUT, "requesting_interface": attached_interface } pr_entry = { "destination_hash": destination_hash, "timeout": time.time()+Transport.PATH_REQUEST_TIMEOUT, "requesting_interface": attached_interface }
Transport.discovery_path_requests[destination_hash] = pr_entry with Transport.discovery_pr_lock: Transport.discovery_path_requests[destination_hash] = pr_entry
for interface in Transport.interfaces: for interface in Transport.interfaces:
if not interface == attached_interface: if not interface == attached_interface:
@@ -2876,11 +2899,11 @@ class Transport:
@staticmethod @staticmethod
def shared_connection_disappeared(): def shared_connection_disappeared():
for link in Transport.active_links: with Transport.active_links_lock:
link.teardown() for link in Transport.active_links: link.teardown()
for link in Transport.pending_links: with Transport.pending_links_lock:
link.teardown() for link in Transport.pending_links: link.teardown()
Transport.announce_table = {} Transport.announce_table = {}
Transport.path_table = {} Transport.path_table = {}
@@ -2892,11 +2915,10 @@ class Transport:
@staticmethod @staticmethod
def shared_connection_reappeared(): def shared_connection_reappeared():
if Transport.owner.is_connected_to_shared_instance: if Transport.owner.is_connected_to_shared_instance:
for registered_destination in Transport.destinations: for registered_destination in Transport.destinations.copy():
if registered_destination.type == RNS.Destination.SINGLE: if registered_destination.type == RNS.Destination.SINGLE:
registered_destination.announce(path_response=True) registered_destination.announce(path_response=True)
@staticmethod @staticmethod
def drop_announce_queues(): def drop_announce_queues():
for interface in Transport.interfaces: for interface in Transport.interfaces: