From 8093c3cd2c71c944e7576800e140d3e1d6ac194b Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 17 Apr 2026 11:39:14 +0200 Subject: [PATCH] Added local destinations lookup map --- RNS/Destination.py | 4 +- RNS/Transport.py | 152 +++++++++++++++++++++++++------------ docs/Reticulum Manual.epub | Bin 4106313 -> 4106312 bytes docs/Reticulum Manual.pdf | Bin 5034265 -> 5034265 bytes 4 files changed, 107 insertions(+), 49 deletions(-) diff --git a/RNS/Destination.py b/RNS/Destination.py index 8729685..a81021c 100755 --- a/RNS/Destination.py +++ b/RNS/Destination.py @@ -411,7 +411,9 @@ class Destination: else: if packet.packet_type == RNS.Packet.DATA: if self.callbacks.packet != None: - try: self.callbacks.packet(plaintext, packet) + try: + def job(): self.callbacks.packet(plaintext, packet) + threading.Thread(target=job, daemon=True).start() except Exception as e: RNS.log("Error while executing receive callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) diff --git a/RNS/Transport.py b/RNS/Transport.py index 79cfd6c..4665de8 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -96,6 +96,7 @@ class Transport: interfaces = [] # All active interfaces destinations = [] # All active destinations + destinations_map = {} # Destination hash map of active destinations pending_links = [] # Links that are being established active_links = [] # Links that are active packet_hashlist = set() # A list of packet hashes for duplicate detection @@ -121,7 +122,9 @@ class Transport: discovery_pr_tags = [] # A table for keeping track of tagged path requests max_pr_tags = 32000 # Maximum amount of unique path request tags to remember + interfaces_lock = Lock() destinations_lock = Lock() + destinations_map_lock = Lock() inbound_announce_lock = Lock() announce_table_lock = Lock() announce_rate_table_lock = Lock() @@ -305,7 +308,8 @@ class Transport: # over an interface. It is cached with it's non- # increased hop-count. announce_packet.hops += 1 - Transport.path_table[destination_hash] = [timestamp, received_from, hops, expires, random_blobs, receiving_interface, announce_packet.packet_hash] + with Transport.path_table_lock: + Transport.path_table[destination_hash] = [timestamp, received_from, hops, expires, random_blobs, receiving_interface, announce_packet.packet_hash] RNS.log("Loaded path table entry for "+RNS.prettyhexrep(destination_hash)+" from storage", RNS.LOG_DEBUG) else: RNS.log("Could not reconstruct path table entry from storage for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG) @@ -410,8 +414,9 @@ class Transport: @staticmethod def prioritize_interfaces(): - try: Transport.interfaces.sort(key=lambda interface: interface.bitrate, reverse=True) - except Exception as e: RNS.log(f"Could not prioritize interfaces according to bitrate. The contained exception was: {e}", RNS.LOG_ERROR) + with Transport.interfaces_lock: + try: Transport.interfaces.sort(key=lambda interface: interface.bitrate, reverse=True) + except Exception as e: RNS.log(f"Could not prioritize interfaces according to bitrate. The contained exception was: {e}", RNS.LOG_ERROR) @staticmethod def enable_discovery(): @@ -610,10 +615,14 @@ class Transport: # Cull invalidated path requests if time.time() > Transport.pending_prs_last_checked+Transport.pending_prs_check_interval: + stale_local_prs = [] with Transport.pending_local_prs_lock: for destination_hash in Transport.pending_local_path_requests: if not Transport.pending_local_path_requests[destination_hash] in Transport.interfaces: - Transport.pending_local_path_requests.pop(destination_hash) + stale_local_prs.append(destination_hash) + + for destination_hash in stale_local_prs: + Transport.pending_local_path_requests.pop(destination_hash) Transport.pending_prs_last_checked = time.time() @@ -1091,8 +1100,10 @@ class Transport: should_transmit = False elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING: - with Transport.destinations_lock: - local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None) + local_destination = None + with Transport.destinations_map_lock: + if packet.destination_hash in Transport.destinations_map: + local_destination = Transport.destinations_map[packet.destination_hash] if local_destination != None: # RNS.log("Allowing announce broadcast on roaming-mode interface from instance-local destination", RNS.LOG_EXTREME) @@ -1114,8 +1125,11 @@ class Transport: should_transmit = False elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY: - with Transport.destinations_lock: - local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None) + local_destination = None + with Transport.destinations_map_lock: + if packet.destination_hash in Transport.destinations_map: + local_destination = Transport.destinations_map[packet.destination_hash] + if local_destination != None: # RNS.log("Allowing announce broadcast on boundary-mode interface from instance-local destination", RNS.LOG_EXTREME) pass @@ -1584,8 +1598,10 @@ class Transport: interface.hold_announce(packet) return - with Transport.destinations_lock: - local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None) + local_destination = None + with Transport.destinations_map_lock: + if packet.destination_hash in Transport.destinations_map: + local_destination = Transport.destinations_map[packet.destination_hash] if local_destination == None and RNS.Identity.validate_announce(packet): if packet.transport_id != None: @@ -1622,7 +1638,9 @@ class Transport: # First, check that the announce is not for a destination # local to this system, and that hops are less than the max - with Transport.destinations_lock: local_and_hops_condition = (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1) + with Transport.destinations_map_lock: + local_and_hops_condition = (packet.hops < Transport.PATHFINDER_M+1) and (not packet.destination_hash in Transport.destinations_map) + if local_and_hops_condition: announce_emitted = Transport.announce_emitted(packet) @@ -1960,31 +1978,35 @@ class Transport: # Handling for link requests to local destinations elif packet.packet_type == RNS.Packet.LINKREQUEST: if packet.transport_id == None or packet.transport_id == Transport.identity.hash: - for destination in Transport.destinations: - if destination.hash == packet.destination_hash and destination.type == packet.destination_type: - path_mtu = RNS.Link.mtu_from_lr_packet(packet) - mode = RNS.Link.mode_from_lr_packet(packet) - if packet.receiving_interface.AUTOCONFIGURE_MTU or packet.receiving_interface.FIXED_MTU: - nh_mtu = packet.receiving_interface.HW_MTU + destination = None + with Transport.destinations_map_lock: + if packet.destination_hash in Transport.destinations_map: + destination = Transport.destinations_map[packet.destination_hash] + + if destination and destination.type == packet.destination_type: + path_mtu = RNS.Link.mtu_from_lr_packet(packet) + mode = RNS.Link.mode_from_lr_packet(packet) + if packet.receiving_interface.AUTOCONFIGURE_MTU or packet.receiving_interface.FIXED_MTU: + nh_mtu = packet.receiving_interface.HW_MTU + else: + nh_mtu = RNS.Reticulum.MTU + + if path_mtu: + if packet.receiving_interface.HW_MTU == None: + path_mtu = None + packet.data = packet.data[:-RNS.Link.LINK_MTU_SIZE] else: - nh_mtu = RNS.Reticulum.MTU + if nh_mtu < path_mtu: + try: + path_mtu = nh_mtu + clamped_mtu = RNS.Link.signalling_bytes(path_mtu, mode) + packet.data = packet.data[:-RNS.Link.LINK_MTU_SIZE]+clamped_mtu + except Exception as e: + RNS.log(f"Dropping link request packet to local destination. The contained exception was: {e}", RNS.LOG_WARNING) + return - if path_mtu: - if packet.receiving_interface.HW_MTU == None: - path_mtu = None - packet.data = packet.data[:-RNS.Link.LINK_MTU_SIZE] - else: - if nh_mtu < path_mtu: - try: - path_mtu = nh_mtu - clamped_mtu = RNS.Link.signalling_bytes(path_mtu, mode) - packet.data = packet.data[:-RNS.Link.LINK_MTU_SIZE]+clamped_mtu - except Exception as e: - RNS.log(f"Dropping link request packet to local destination. The contained exception was: {e}", RNS.LOG_WARNING) - return - - packet.destination = destination - destination.receive(packet) + packet.destination = destination + destination.receive(packet) # Handling for local data packets elif packet.packet_type == RNS.Packet.DATA: @@ -2015,19 +2037,22 @@ class Transport: while packet.packet_hash in Transport.packet_hashlist: Transport.packet_hashlist.remove(packet.packet_hash) else: - for destination in Transport.destinations: - if destination.hash == packet.destination_hash and destination.type == packet.destination_type: - packet.destination = destination - if destination.receive(packet): - if destination.proof_strategy == RNS.Destination.PROVE_ALL: packet.prove() + destination = None + with Transport.destinations_map_lock: + if packet.destination_hash in Transport.destinations_map: + destination = Transport.destinations_map[packet.destination_hash] - elif destination.proof_strategy == RNS.Destination.PROVE_APP: - if destination.callbacks.proof_requested: - try: - if destination.callbacks.proof_requested(packet): packet.prove() - except Exception as e: - RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR) - break + if destination and destination.type == packet.destination_type: + packet.destination = destination + if destination.receive(packet): + if destination.proof_strategy == RNS.Destination.PROVE_ALL: packet.prove() + + elif destination.proof_strategy == RNS.Destination.PROVE_APP: + if destination.callbacks.proof_requested: + try: + if destination.callbacks.proof_requested(packet): packet.prove() + except Exception as e: + RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR) # Handling for proofs and link-request proofs elif packet.packet_type == RNS.Packet.PROOF: @@ -2254,6 +2279,27 @@ class Transport: RNS.log("Removing path to "+RNS.prettyhexrep(deprecated_path)+" from tunnel "+RNS.prettyhexrep(tunnel_id), RNS.LOG_DEBUG) with Transport.tunnels_lock: paths.pop(deprecated_path) + @staticmethod + def clean_destinations_map(): + with Transport.destinations_lock: + for destination in Transport.destinations: + with Transport.destinations_map_lock: + if not destination.hash in Transport.destinations_map: + Transport.destinations_map[destination.hash] = destination + + with Transport.destinations_map_lock: + stale_destination_hashes = [] + for destination_hash in Transport.destinations_map: + with Transport.destinations_lock: + found = False + for destination in Transport.destinations: + if destination.hash == destination_hash: found = True + + if not found: stale_destination_hashes.append(destination_hash) + + for destination_hash in stale_destination_hashes: + Transport.destinations_map.pop(destination_hash) + @staticmethod def register_destination(destination): destination.MTU = RNS.Reticulum.MTU @@ -2265,6 +2311,9 @@ class Transport: Transport.destinations.append(destination) + with Transport.destinations_map_lock: + Transport.destinations_map[destination.hash] = destination + if Transport.owner.is_connected_to_shared_instance: if destination.type == RNS.Destination.SINGLE: def job(): @@ -2277,6 +2326,10 @@ class Transport: with Transport.destinations_lock: if destination in Transport.destinations: Transport.destinations.remove(destination) + with Transport.destinations_map_lock: + if destination.hash in Transport.destinations_map: + Transport.destinations_map.pop(destination.hash) + @staticmethod def register_link(link): RNS.log("Registering link "+str(link), RNS.LOG_EXTREME) @@ -2761,8 +2814,11 @@ class Transport: destination_exists_on_local_client = True with Transport.pending_local_prs_lock: Transport.pending_local_path_requests[destination_hash] = attached_interface - - local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None) + + local_destination = None + with Transport.destinations_map_lock: + if destination_hash in Transport.destinations_map: local_destination = Transport.destinations_map[destination_hash] + if local_destination != None: local_destination.announce(path_response=True, tag=tag, attached_interface=attached_interface) RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", destination is local to this system", RNS.LOG_DEBUG) diff --git a/docs/Reticulum Manual.epub b/docs/Reticulum Manual.epub index 7172db6069f6051d2d570fb97e4487939442385f..2dc91ebffcb0b60f52aa798c6993b194a0aad08e 100644 GIT binary patch delta 2650 zcmZA32|QHW9{})sXAojcWN9p8#!jL5Bg?h#692qvL^ZZ#Un5JCNF`0SIv9pwP@>o7 z=^yH;UZpJ2`lqKw^< zY_#7pDTE*?3J9_sL6E@csD$9C1pVlkkj9b?@fS_Vw{Iur%(|x4!ca7z{ zIong1SzB{8RlQxG@++?1J9<7axBN~AEzrHix{?2Rv}=_53zw0amm_xde0oyAw%<=5 zUiCb3?Ai`iB6plRP?u;stoYvy*1^Yqkz+oi0dMvmy3rmSn}6AEqIKswMoN{-m`s%K z(EZ>O`IKeP_FZu6URu-6TpQCREfN<97LOgZ_h-1cY;8KvP`}CXW%yg9+PeCLI9zS< z07^yfIdme(c~%(vs_E^n5RwliSvf22+(vF&`h|u>Qt3^H!r$cea%Vj^-rsWibGULq z|A2f=CuN{GCC`>!?#-Q?k92>!*@NQ=%tE)l2%lU`jpmhf?_AIdb-LHk+4$U+nw55O zsn6`$v5n!q7B?$T1s%M_j%@J`@9e&p8mOBQN^35RCuj)y*FQ2TEF;iZ?g-&)a}vE$l{!<$c^?Q&u! zCc2MYxnR9)q@JAImNqT z(rcIIeK6|vEM#hov!w(zVio)Ca+@~U=8-N_Wo`GGo}IqaPU5Ug(ck`Kzyh#n81MY%U%2NanbDedvf+c&oz)^_r* zf9PD4?UuUXooMJDu_~61z7Jb^)lvVZYvxT`(`M+#R71!26Am?x4{+AWKPgt89&OuB z3a#m*o2pvvC>RMKcHh&`VwQ9)8CCclh`Z$;Y&UJ->KGy-E>ii?e@67vY;38N^o}^$ zkN~ag|M_xc(_p^^tA%mqK?30$Ucl6DQ(@2O}P2>SbSN^XdfwHcnyT9)R=zIJF@PaL%?VH<+Jf$Mg}|&O;(2g zx}d>au=a8%3ax}sGf{*HA95GXMJSqhux0fS8xO2nZlHKTY}+e=2doY=BRsgW_(4Vx zG+!j6ang9Ct%f$c;L)paR7Mz&KEGuB8?;&kkE+JdsSS8^ z!klp19*=hHBiNAz(IGyY01+V}NEi}4aq<&Az4Tc zl7|$aRS*SIgp?pxclD^ zeugVpTZ$>;op_gGil_mIEX7ptI$Kbm1@+(0--UHTY)2uxgCg})0zl@#whdr4=UZPM zur5?XMiFqX6qBb4#_**iPAJy}L1r(Z2>JT}V9C#*03HXU;*CZ%vJFPzOLx6Ef=K?* z=plH{!IbcUEeVp107}`nWaeeCz5q6tVJbR;Os&_l{@oylAeCOoPqUTlL6P|Akc1>= z9AK7VGI-9LB4$RZaP1^v!XI-^fS$5%IVS`;ihy`IM#slsP!8*dfO|PcVF+@*=nCAe z2&a{zhaeI^4BUGk0Z9sFChU&aPYz9pjQDBk{Mg4M$J<|J&q(h6>Cc9RyQ?VlyzV#2nC7V6UKf53a(3$kn5PRy~>mQ08H} zs2p(NVIHVIIKhKW(m=nUcn7Fem@OI#m{phyx((DtKs^|5)ef2`}uzGGj4#2IylE-@Ujlm!aH1k g3R6^z%D|8)=AQ3+D-3TW1hIf0St$fjF2P{!A9oNe4*&oF delta 2658 zcmZ{mcUTk48i#iiT9DpL2!s|cDpl#M62XfoN>TA(0}&325{xt(A%Is92q2Il^j=i3 zq1aF<9*N>53Ld0L1W~Stq9BN(cqe=G56^S&{nV>-$rudTDyh?nNv$|AUvW_|~UZjp@iqg1c@b(${7Z_@jNwZo5h% zy0uQ2JxJ@Zdp?`Ji8;$4$)&QxxE7gVH%1-JZ)eEMQc{ffn{zeZ6cv~H`Ofz` zv^O@Hd+?TDv7FIMBs*3(31J z`p4`)QoKD$dJZ$umS zJYEwKpWFQCV$y~=x3u2VUmKl6k~*wTY?K+eV`9m7kDdx!@q`(A*~M<{%GYxIWup&W zBmDa|pS-wjz~8IsbN;H%M z-a0hDo@pZ%^iTQvLwX5AnL(49F%pXx4pVk@g zN&dn-;b6_awd2HyP2%suoA+h%SS_OtzNen;vz+lvUXaR4)tF_IEU&Kbxy7(bYl#%I z-Q=r0xKF0(lcx_YFJmfA$w?#2^PzRVP_gAdQtsic+F2fLiw?+gV>eh1b86l0t&EYl z`8wIhtER_K2)tgLeVpAXz8C)>|bfHg!F-n3~xTAFKdu3B}wO-R(BTSWz&cGnU_;!s?yIaD3+si&48Pk$4Y#@v z$SiFmQuL79OY#;gU*Eh&C@2$SA_04Y$K&N>Mc=4^M1l;hoh3) z4?Qy{6?{j%))K{3;Ok6E41sUCOZF04#zc|WyyOK37fv_YP+Sn256R#nj&Em;i)&wJ zl|`cIq5{fP!WGQ`ZF0xea2TpAj;l`*sGJ_Iq_R*A3|9tS=o2+u&A&t|Byd$Wfxh>| zRgMGU-YQ(}h$GMyMCwEZhX5f$#314j35X;_3POTNLl!|~AhHlSh&)6Aq6krfC__{r zst`4ZIz$7a3DJURL&y*v2nC`G(SuMS`Va$%A;bt`452|xApd}vLKZ`oK$b$xAm$JY zh$X}dVhvdaSq`y**h1_e_7De%V^amkX($8FJ&EFc_OJR{TRG>;;#Cl=aBdQ;z-nFa z5l7gVi3SN)=dZ65ILpHf+ggZaAFp!| zq?#*^$idzJ*(%u{z#<>h!58t$$FxxkurD8@;Qe$_e-_mJyw8Vy0+XMe&e!4=VEEYp z@&#~AJy=}uV~w@2FIKI9BH&y;rmidU{hh}YOLIq%nMM>*_~{thO13sAEx>f~L2r!y zW&Q?!ZSAEIMD~|KPry(CM#cmBCUW)$Xcqo(W?zVDv%Y4c2s1b&m>C=%6l@k78o7Vp z-?P7YJIUv)8iJI1A%DwWy$eMSGD0F_0-673Zo7n?^$}QDSs2x?fla=IbE80aA*PN; z_ZCJ&%$-2Ng$Yyz=obB`kXaE7IvIErVOq|jay)++v_~7(;qYPvk@+Q4pEv>%6B-z~ z=ivWL1C)tY0-(7FvmtifCjggXOasUjV;1OZu%a0AAa=Hp@Qd75k~pX<#!QL3bY(!C zt_&GGslXJ#%Xt|E(O((7RkEJ~of2#{ag?sWU8SG};!7}7G##8N!B(L{Fex&Az`7K3 zMYTYz$V`D|ktqR$GHBGnKg+On=njx0>Ky}pA~Oys<(Lb)2LzO3?&vm9R}L@gfp;R~ z53DQTnq449WK6(~3V87yw-R$hrNQb-j81IxcK||v2USp731d$ZcLb&4j;dgJIgEX( z64N5)xI2OK?oJf2wCYE7ovL6>V!@Uw3?p_gb_SD+oi)IPDws(Z7_5R0CIGb>x^n@+ z)fg4ur<7_qg$AmsF=vzkUWm*cK&`8o?|(!SKLFP;X*^S~HF2*Vh`D8?|3 zFd~Rz0+X1+G-eP(9J83iJQlEsB`hO>6(q5W6xOf~4QXs(6ILYMGkrF;`ezE z1qhgsC_=%4jS?J`Q9%_o?4ynYG|+?#4?YeN;0VV!!70xA1EW=a%$0-?j#POebs_X@ zv~b?O^KU!%TE?$PA#I_|zodWl)_RC|zG;=6vaftkDbp%?o^l*Fkb!NPZqt-@U90_< K^y43`aOwwkv$Mqj delta 460 zcmajZxi5oZ0D$qRtF5ASU*&W5^6A^R-_=9)yXq>7flVaL7Kz1XFg39@Hls;G(!~E@ zHyQj1-)!(m?1ta=3{U&0mLwfAq*pq1V%85F^ppZlbFIZW-yC6Br%T#C|JZ2ma&3WtRaOoGRPu_b^JbW zAP<28B#O|W!+;43C6rM?6`QDG3w79V;KGBC2DY(-UF>1M6R6F~O;#^hx~9pTkSQTj znP~pt^4vSSx=_+ySqf5m$Of$u$kfv~1h4Y+>4tCmN3D7k!~wLe`Y( Me@Q3t+6*Uu0N<^)xc~qF