Improved path response handling. Prepared destination path response handling for multi-path Transport.

This commit is contained in:
Mark Qvist 2022-12-22 11:28:56 +01:00
parent dd7931d421
commit b305eb8e0a
2 changed files with 51 additions and 23 deletions

View File

@ -69,6 +69,8 @@ class Destination:
OUT = 0x12; OUT = 0x12;
directions = [IN, OUT] directions = [IN, OUT]
PR_TAG_WINDOW = 30
@staticmethod @staticmethod
def expand_name(identity, app_name, *aspects): def expand_name(identity, app_name, *aspects):
""" """
@ -132,6 +134,7 @@ class Destination:
self.proof_strategy = Destination.PROVE_NONE self.proof_strategy = Destination.PROVE_NONE
self.mtu = 0 self.mtu = 0
self.path_responses = {}
self.links = [] self.links = []
if identity == None and direction == Destination.IN and self.type != Destination.PLAIN: if identity == None and direction == Destination.IN and self.type != Destination.PLAIN:
@ -163,7 +166,7 @@ class Destination:
return "<"+self.name+"/"+self.hexhash+">" return "<"+self.name+"/"+self.hexhash+">"
def announce(self, app_data=None, path_response=False, send=True): def announce(self, app_data=None, path_response=False, attached_interface=None, tag=None, send=True):
""" """
Creates an announce packet for this destination and broadcasts it on all Creates an announce packet for this destination and broadcasts it on all
relevant interfaces. Application specific data can be added to the announce. relevant interfaces. Application specific data can be added to the announce.
@ -173,35 +176,60 @@ class Destination:
""" """
if self.type != Destination.SINGLE: if self.type != Destination.SINGLE:
raise TypeError("Only SINGLE destination types can be announced") raise TypeError("Only SINGLE destination types can be announced")
destination_hash = self.hash
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
if app_data == None and self.default_app_data != None:
if isinstance(self.default_app_data, bytes):
app_data = self.default_app_data
elif callable(self.default_app_data):
returned_app_data = self.default_app_data()
if isinstance(returned_app_data, bytes):
app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash now = time.time()
if app_data != None: stale_responses = []
signed_data += app_data for entry_tag in self.path_responses:
entry = self.path_responses[entry_tag]
if now > entry[0]+Destination.PR_TAG_WINDOW:
stale_responses.append(entry_tag)
signature = self.identity.sign(signed_data) for entry_tag in stale_responses:
self.path_responses.pop(entry_tag)
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+signature if (path_response == True and tag != None) and tag in self.path_responses:
# This code is currently not used, since Transport will block duplicate
# path requests based on tags. When multi-path support is implemented in
# Transport, this will allow Transport to detect redundant paths to the
# same destination, and select the best one based on chosen criteria,
# since it will be able to detect that a single emitted announce was
# received via multiple paths. The difference in reception time will
# potentially also be useful in determining characteristics of the
# multiple available paths, and to choose the best one.
RNS.log("Using cached announce data for answering path request with tag "+RNS.prettyhexrep(tag), RNS.LOG_EXTREME)
announce_data = self.path_responses[tag][1]
else:
destination_hash = self.hash
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
if app_data != None: if app_data == None and self.default_app_data != None:
announce_data += app_data if isinstance(self.default_app_data, bytes):
app_data = self.default_app_data
elif callable(self.default_app_data):
returned_app_data = self.default_app_data()
if isinstance(returned_app_data, bytes):
app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash
if app_data != None:
signed_data += app_data
signature = self.identity.sign(signed_data)
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+signature
if app_data != None:
announce_data += app_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: else:
announce_context = RNS.Packet.NONE announce_context = RNS.Packet.NONE
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)
if send: if send:
announce_packet.send() announce_packet.send()

View File

@ -1948,10 +1948,9 @@ class Transport:
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None) local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
if local_destination != None: if local_destination != None:
local_destination.announce(path_response=True) 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) RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", destination is local to this system", RNS.LOG_DEBUG)
elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table): elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table):
packet = Transport.destination_table[destination_hash][6] packet = Transport.destination_table[destination_hash][6]
next_hop = Transport.destination_table[destination_hash][1] next_hop = Transport.destination_table[destination_hash][1]
@ -1996,9 +1995,10 @@ class Transport:
# Forward path request on all interfaces # Forward path request on all interfaces
# except the local client # except the local client
RNS.log("Forwarding path request from local client for "+RNS.prettyhexrep(destination_hash)+interface_str+" to all other interfaces", RNS.LOG_DEBUG) RNS.log("Forwarding path request from local client for "+RNS.prettyhexrep(destination_hash)+interface_str+" to all other interfaces", RNS.LOG_DEBUG)
request_tag = RNS.Identity.get_random_hash()
for interface in Transport.interfaces: for interface in Transport.interfaces:
if not interface == attached_interface: if not interface == attached_interface:
Transport.request_path(destination_hash, interface) Transport.request_path(destination_hash, interface, tag = request_tag)
elif should_search_for_unknown: elif should_search_for_unknown:
if destination_hash in Transport.discovery_path_requests: if destination_hash in Transport.discovery_path_requests: