kopia lustrzana https://github.com/markqvist/reticulum
				
				
				
			Improved path re-discovery in changing topographies
							rodzic
							
								
									67c7395ea7
								
							
						
					
					
						commit
						b3731524ac
					
				
							
								
								
									
										114
									
								
								RNS/Transport.py
								
								
								
								
							
							
						
						
									
										114
									
								
								RNS/Transport.py
								
								
								
								
							| 
						 | 
				
			
			@ -68,6 +68,10 @@ class Transport:
 | 
			
		|||
    PATH_REQUEST_RW      = 2            # Path request random window
 | 
			
		||||
    PATH_REQUEST_MI      = 5            # Minimum interval in seconds for automated path requests
 | 
			
		||||
 | 
			
		||||
    STATE_UNKNOWN        = 0x00
 | 
			
		||||
    STATE_UNRESPONSIVE   = 0x01
 | 
			
		||||
    STATE_RESPONSIVE     = 0x02
 | 
			
		||||
 | 
			
		||||
    LINK_TIMEOUT         = RNS.Link.STALE_TIME * 1.25
 | 
			
		||||
    REVERSE_TIMEOUT      = 30*60        # Reverse table entries are removed after 30 minutes
 | 
			
		||||
    DESTINATION_TIMEOUT  = 60*60*24*7   # Destination table entries are removed if unused for one week
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +98,7 @@ class Transport:
 | 
			
		|||
    tunnels              = {}           # A table storing tunnels to other transport instances
 | 
			
		||||
    announce_rate_table  = {}           # A table for keeping track of announce rates
 | 
			
		||||
    path_requests        = {}           # A table for storing path request timestamps
 | 
			
		||||
    path_states          = {}           # A table for keeping track of path states
 | 
			
		||||
    
 | 
			
		||||
    discovery_path_requests  = {}       # A table for keeping track of path requests on behalf of other nodes
 | 
			
		||||
    discovery_pr_tags        = []       # A table for keeping track of tagged path requests
 | 
			
		||||
| 
						 | 
				
			
			@ -422,6 +427,12 @@ class Transport:
 | 
			
		|||
                    Transport.discovery_pr_tags = Transport.discovery_pr_tags[len(Transport.discovery_pr_tags)-Transport.max_pr_tags:len(Transport.discovery_pr_tags)-1]
 | 
			
		||||
 | 
			
		||||
                if time.time() > Transport.tables_last_culled + Transport.tables_cull_interval:
 | 
			
		||||
                    # Remove unneeded path state entries
 | 
			
		||||
                    stale_path_states = []
 | 
			
		||||
                    for destination_hash in Transport.path_states:
 | 
			
		||||
                        if not destination_hash in Transport.destination_table:
 | 
			
		||||
                            stale_path_states.append(destination_hash)
 | 
			
		||||
 | 
			
		||||
                    # Cull the reverse table according to timeout
 | 
			
		||||
                    stale_reverse_entries = []
 | 
			
		||||
                    for truncated_packet_hash in Transport.reverse_table:
 | 
			
		||||
| 
						 | 
				
			
			@ -471,14 +482,18 @@ class Transport:
 | 
			
		|||
                                    RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and destination was previously local to an interface on this instance", RNS.LOG_DEBUG)
 | 
			
		||||
                                    path_request_conditions = True
 | 
			
		||||
 | 
			
		||||
                                # If the link destination was previously only 1 hop
 | 
			
		||||
                                # away, this likely means that it was local to one
 | 
			
		||||
                                # of our interfaces, and that it roamed somewhere else.
 | 
			
		||||
                                # In that case, try to discover a new path.
 | 
			
		||||
                                # If the link initiator was previously only 1 hop
 | 
			
		||||
                                # away, this likely means that network topology has
 | 
			
		||||
                                # changed. In that case, we try to discover a new path,
 | 
			
		||||
                                # and mark the old one as potentially unresponsive.
 | 
			
		||||
                                elif not path_request_throttle and lr_taken_hops == 1:
 | 
			
		||||
                                    RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and link initiator is local to an interface on this instance", RNS.LOG_DEBUG)
 | 
			
		||||
                                    path_request_conditions = True
 | 
			
		||||
 | 
			
		||||
                                    if RNS.Reticulum.transport_enabled():
 | 
			
		||||
                                        if hasattr(link_entry[4], "mode") and link_entry[4].mode != RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
 | 
			
		||||
                                            Transport.mark_path_unresponsive(link_entry[6])
 | 
			
		||||
 | 
			
		||||
                                if path_request_conditions:
 | 
			
		||||
                                    if not link_entry[6] in path_requests:
 | 
			
		||||
                                        path_requests.append(link_entry[6])
 | 
			
		||||
| 
						 | 
				
			
			@ -549,8 +564,6 @@ class Transport:
 | 
			
		|||
                        else:
 | 
			
		||||
                            RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    
 | 
			
		||||
                    i = 0
 | 
			
		||||
                    for truncated_packet_hash in stale_reverse_entries:
 | 
			
		||||
                        Transport.reverse_table.pop(truncated_packet_hash)
 | 
			
		||||
| 
						 | 
				
			
			@ -562,8 +575,6 @@ class Transport:
 | 
			
		|||
                        else:
 | 
			
		||||
                            RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME)
 | 
			
		||||
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    i = 0
 | 
			
		||||
                    for link_id in stale_links:
 | 
			
		||||
                        Transport.link_table.pop(link_id)
 | 
			
		||||
| 
						 | 
				
			
			@ -608,6 +619,17 @@ class Transport:
 | 
			
		|||
                        else:
 | 
			
		||||
                            RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_EXTREME)
 | 
			
		||||
 | 
			
		||||
                    i = 0
 | 
			
		||||
                    for destination_hash in stale_path_states:
 | 
			
		||||
                        Transport.path_states.pop(destination_hash)
 | 
			
		||||
                        i += 1
 | 
			
		||||
 | 
			
		||||
                    if i > 0:
 | 
			
		||||
                        if i == 1:
 | 
			
		||||
                            RNS.log("Removed "+str(i)+" path state entry", RNS.LOG_EXTREME)
 | 
			
		||||
                        else:
 | 
			
		||||
                            RNS.log("Removed "+str(i)+" path state entries", RNS.LOG_EXTREME)
 | 
			
		||||
 | 
			
		||||
                    Transport.tables_last_culled = time.time()
 | 
			
		||||
 | 
			
		||||
                if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
 | 
			
		||||
| 
						 | 
				
			
			@ -1319,8 +1341,10 @@ class Transport:
 | 
			
		|||
                                    if path_announce_emitted >= announce_emitted:
 | 
			
		||||
                                        break
 | 
			
		||||
 | 
			
		||||
                                # If the path has expired, consider this
 | 
			
		||||
                                # announce for adding to the path table.
 | 
			
		||||
                                if (now >= path_expires):
 | 
			
		||||
                                    # We also check that the announce is
 | 
			
		||||
                                    # We check that the announce is
 | 
			
		||||
                                    # different from ones we've already heard,
 | 
			
		||||
                                    # to avoid loops in the network
 | 
			
		||||
                                    if not random_blob in random_blobs:
 | 
			
		||||
| 
						 | 
				
			
			@ -1331,12 +1355,26 @@ class Transport:
 | 
			
		|||
                                    else:
 | 
			
		||||
                                        should_add = False
 | 
			
		||||
                                else:
 | 
			
		||||
                                    # If the path is not expired, but the emission
 | 
			
		||||
                                    # is more recent, and we haven't already heard
 | 
			
		||||
                                    # this announce before, update the path table.
 | 
			
		||||
                                    if (announce_emitted > path_announce_emitted):
 | 
			
		||||
                                        if not random_blob in random_blobs:
 | 
			
		||||
                                            RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since it was more recently emitted", RNS.LOG_DEBUG)
 | 
			
		||||
                                            should_add = True
 | 
			
		||||
                                        else:
 | 
			
		||||
                                            should_add = False
 | 
			
		||||
                                    
 | 
			
		||||
                                    # If we have already heard this announce before,
 | 
			
		||||
                                    # but the path has been marked as unresponsive
 | 
			
		||||
                                    # by a failed communications attempt or similar,
 | 
			
		||||
                                    # allow updating the path table to this one.
 | 
			
		||||
                                    elif announce_emitted == path_announce_emitted:
 | 
			
		||||
                                        if Transport.path_is_unresponsive(packet.destination_hash):
 | 
			
		||||
                                            RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since previously tried path was unresponsive", RNS.LOG_DEBUG)
 | 
			
		||||
                                            should_add = True
 | 
			
		||||
                                        else:
 | 
			
		||||
                                            should_add = False
 | 
			
		||||
 | 
			
		||||
                        else:
 | 
			
		||||
                            # If this destination is unknown in our table
 | 
			
		||||
| 
						 | 
				
			
			@ -1601,32 +1639,35 @@ class Transport:
 | 
			
		|||
                    # needs to be transported
 | 
			
		||||
                    if (RNS.Reticulum.transport_enabled() or for_local_client_link or from_local_client) and packet.destination_hash in Transport.link_table:
 | 
			
		||||
                        link_entry = Transport.link_table[packet.destination_hash]
 | 
			
		||||
                        if packet.receiving_interface == link_entry[2]:
 | 
			
		||||
                            try:
 | 
			
		||||
                                if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
 | 
			
		||||
                                    peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
 | 
			
		||||
                                    peer_identity = RNS.Identity.recall(link_entry[6])
 | 
			
		||||
                                    peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
 | 
			
		||||
                        if packet.hops == link_entry[3]:
 | 
			
		||||
                            if packet.receiving_interface == link_entry[2]:
 | 
			
		||||
                                try:
 | 
			
		||||
                                    if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
 | 
			
		||||
                                        peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
 | 
			
		||||
                                        peer_identity = RNS.Identity.recall(link_entry[6])
 | 
			
		||||
                                        peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
 | 
			
		||||
 | 
			
		||||
                                    signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
 | 
			
		||||
                                    signature = packet.data[:RNS.Identity.SIGLENGTH//8]
 | 
			
		||||
                                        signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
 | 
			
		||||
                                        signature = packet.data[:RNS.Identity.SIGLENGTH//8]
 | 
			
		||||
 | 
			
		||||
                                    if peer_identity.validate(signature, signed_data):
 | 
			
		||||
                                        RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
 | 
			
		||||
                                        new_raw = packet.raw[0:1]
 | 
			
		||||
                                        new_raw += struct.pack("!B", packet.hops)
 | 
			
		||||
                                        new_raw += packet.raw[2:]
 | 
			
		||||
                                        Transport.link_table[packet.destination_hash][7] = True
 | 
			
		||||
                                        Transport.transmit(link_entry[4], new_raw)
 | 
			
		||||
                                        if peer_identity.validate(signature, signed_data):
 | 
			
		||||
                                            RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
 | 
			
		||||
                                            new_raw = packet.raw[0:1]
 | 
			
		||||
                                            new_raw += struct.pack("!B", packet.hops)
 | 
			
		||||
                                            new_raw += packet.raw[2:]
 | 
			
		||||
                                            Transport.link_table[packet.destination_hash][7] = True
 | 
			
		||||
                                            Transport.transmit(link_entry[4], new_raw)
 | 
			
		||||
 | 
			
		||||
                                    else:
 | 
			
		||||
                                        RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
 | 
			
		||||
                                        else:
 | 
			
		||||
                                            RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
 | 
			
		||||
 | 
			
		||||
                            except Exception as e:
 | 
			
		||||
                                RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
 | 
			
		||||
                                except Exception as e:
 | 
			
		||||
                                    RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
 | 
			
		||||
 | 
			
		||||
                            else:
 | 
			
		||||
                                RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
 | 
			
		||||
                        else:
 | 
			
		||||
                            RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
 | 
			
		||||
                            RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG)
 | 
			
		||||
                    else:
 | 
			
		||||
                        # Check if we can deliver it to a local
 | 
			
		||||
                        # pending link
 | 
			
		||||
| 
						 | 
				
			
			@ -1984,6 +2025,21 @@ class Transport:
 | 
			
		|||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def mark_path_unresponsive(destination_hash):
 | 
			
		||||
        if destination_hash in Transport.destination_table:
 | 
			
		||||
            Transport.path_states[destination_hash] = Transport.STATE_UNRESPONSIVE
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def path_is_unresponsive(destination_hash):
 | 
			
		||||
        if destination_hash in Transport.path_states:
 | 
			
		||||
            if Transport.path_states[destination_hash] == Transport.STATE_UNRESPONSIVE:
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def request_path(destination_hash, on_interface=None, tag=None, recursive=False):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue