From 1391c9ef3be31c4444f7cf21c3c694116b1dea65 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 27 Apr 2020 12:04:14 +0200 Subject: [PATCH] Made running Transport a config choice --- Examples/Echo.py | 3 +- RNS/Reticulum.py | 22 +++-- RNS/Transport.py | 239 ++++++++++++++++++++++++----------------------- 3 files changed, 139 insertions(+), 125 deletions(-) diff --git a/Examples/Echo.py b/Examples/Echo.py index e737b40..a56e19d 100644 --- a/Examples/Echo.py +++ b/Examples/Echo.py @@ -96,7 +96,8 @@ def client(destination_hexhash, configpath, timeout=None): # We override the loglevel to provide feedback when # an announce is received - RNS.loglevel = RNS.LOG_INFO + if RNS.loglevel < RNS.LOG_INFO: + RNS.loglevel = RNS.LOG_INFO # Tell the user that the client is ready! RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)") diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index 1125bee..e2ba5d5 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -40,6 +40,7 @@ class Reticulum: Reticulum.cachepath = Reticulum.configdir+"/storage/cache" Reticulum.__allow_unencrypted = False + Reticulum.__transport_enabled = False Reticulum.__use_implicit_proof = True if not os.path.isdir(Reticulum.storagepath): @@ -60,7 +61,6 @@ class Reticulum: self.applyConfig() RNS.Identity.loadKnownDestinations() - Reticulum.router = self RNS.Transport.start() @@ -80,22 +80,28 @@ class Reticulum: if "reticulum" in self.config: for option in self.config["reticulum"]: value = self.config["reticulum"][option] + if option == "enable_transport": + v = self.config["reticulum"].as_bool(option) + if v == True: + Reticulum.__transport_enabled = True if option == "use_implicit_proof": - if value == "true": + v = self.config["reticulum"].as_bool(option) + if v == True: Reticulum.__use_implicit_proof = True - if value == "false": + if v == False: Reticulum.__use_implicit_proof = False if option == "allow_unencrypted": - if value == "true": + v = self.config["reticulum"].as_bool(option) + if v == True: RNS.log("", RNS.LOG_CRITICAL) - RNS.log("! ! !", RNS.LOG_CRITICAL) + RNS.log("! ! ! ! ! ! ! ! !", RNS.LOG_CRITICAL) RNS.log("", RNS.LOG_CRITICAL) RNS.log("Danger! Encryptionless links have been allowed in the config file!", RNS.LOG_CRITICAL) RNS.log("Beware of the consequences! Any data sent over a link can potentially be intercepted,", RNS.LOG_CRITICAL) RNS.log("read and modified! If you are not absolutely sure that you want this,", RNS.LOG_CRITICAL) RNS.log("you should exit Reticulum NOW and change your config file!", RNS.LOG_CRITICAL) RNS.log("", RNS.LOG_CRITICAL) - RNS.log("! ! !", RNS.LOG_CRITICAL) + RNS.log("! ! ! ! ! ! ! ! !", RNS.LOG_CRITICAL) RNS.log("", RNS.LOG_CRITICAL) Reticulum.__allow_unencrypted = True @@ -286,6 +292,10 @@ class Reticulum: def should_use_implicit_proof(): return Reticulum.__use_implicit_proof + @staticmethod + def transport_enabled(): + return Reticulum.__transport_enabled + # Default configuration file: __default_rns_config__ = '''# This is the default Reticulum config file. # You should probably edit it to include any additional, diff --git a/RNS/Transport.py b/RNS/Transport.py index 46c35ca..62ca0ce 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -90,15 +90,17 @@ class Transport: except Exception as e: RNS.log("Could not load packet hashlist from disk, the contained exception was: "+str(e), RNS.LOG_ERROR) - # Create transport-specific destinations - path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") - path_request_destination.packet_callback(Transport.pathRequestHandler) + if RNS.Reticulum.transport_enabled(): + # Create transport-specific destinations + path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") + path_request_destination.packet_callback(Transport.pathRequestHandler) thread = threading.Thread(target=Transport.jobloop) thread.setDaemon(True) thread.start() - RNS.log("Transport instance "+str(Transport.identity)+" started") + if RNS.Reticulum.transport_enabled(): + RNS.log("Transport instance "+str(Transport.identity)+" started") @staticmethod def jobloop(): @@ -123,34 +125,35 @@ class Transport: Transport.receipts_last_checked = time.time() - # Process announces needing retransmission - if time.time() > Transport.announces_last_checked+Transport.announces_check_interval: - for destination_hash in Transport.announce_table: - announce_entry = Transport.announce_table[destination_hash] - if announce_entry[2] > Transport.PATHFINDER_R: - RNS.log("Dropping announce for "+RNS.prettyhexrep(destination_hash)+", retries exceeded", RNS.LOG_DEBUG) - Transport.announce_table.pop(destination_hash) - break - else: - if time.time() > announce_entry[1]: - announce_entry[1] = time.time() + math.pow(Transport.PATHFINDER_C, announce_entry[4]) + Transport.PATHFINDER_T + Transport.PATHFINDER_RW - announce_entry[2] += 1 - packet = announce_entry[5] - block_rebroadcasts = announce_entry[7] - announce_context = RNS.Packet.NONE - if block_rebroadcasts: - announce_context = RNS.Packet.PATH_RESPONSE - announce_data = packet.data - announce_identity = RNS.Identity.recall(packet.destination_hash) - announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown"); - announce_destination.hash = packet.destination_hash - announce_destination.hexhash = announce_destination.hash.hex() - new_packet = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, header_type = RNS.Packet.HEADER_2, transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash) - new_packet.hops = announce_entry[4] - RNS.log("Rebroadcasting announce for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG) - outgoing.append(new_packet) + if RNS.Reticulum.transport_enabled(): + # Process announces needing retransmission + if time.time() > Transport.announces_last_checked+Transport.announces_check_interval: + for destination_hash in Transport.announce_table: + announce_entry = Transport.announce_table[destination_hash] + if announce_entry[2] > Transport.PATHFINDER_R: + RNS.log("Dropping announce for "+RNS.prettyhexrep(destination_hash)+", retries exceeded", RNS.LOG_DEBUG) + Transport.announce_table.pop(destination_hash) + break + else: + if time.time() > announce_entry[1]: + announce_entry[1] = time.time() + math.pow(Transport.PATHFINDER_C, announce_entry[4]) + Transport.PATHFINDER_T + Transport.PATHFINDER_RW + announce_entry[2] += 1 + packet = announce_entry[5] + block_rebroadcasts = announce_entry[7] + announce_context = RNS.Packet.NONE + if block_rebroadcasts: + announce_context = RNS.Packet.PATH_RESPONSE + announce_data = packet.data + announce_identity = RNS.Identity.recall(packet.destination_hash) + announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown"); + announce_destination.hash = packet.destination_hash + announce_destination.hexhash = announce_destination.hash.hex() + new_packet = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, header_type = RNS.Packet.HEADER_2, transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash) + new_packet.hops = announce_entry[4] + RNS.log("Rebroadcasting announce for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG) + outgoing.append(new_packet) - Transport.announces_last_checked = time.time() + Transport.announces_last_checked = time.time() # Cull the packet hashlist if it has reached max size @@ -170,7 +173,7 @@ class Transport: if time.time() > link_entry[0] + Transport.LINK_TIMEOUT: Transport.link_table.pop(link_id) - # Cull the destination table in some way + # Cull the destination table for destination_hash in Transport.destination_table: destination_entry = Transport.destination_table[destination_hash] if time.time() > destination_entry[0] + Transport.DESTINATION_TIMEOUT: @@ -295,95 +298,96 @@ class Transport: # General transport handling. Takes care of directing # packets according to transport tables and recording # entries in reverse and link tables. - if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE: - if packet.transport_id == Transport.identity.hash: - RNS.log("Received packet in transport for "+RNS.prettyhexrep(packet.destination_hash)+" with matching transport ID, transporting it...", RNS.LOG_DEBUG) - if packet.destination_hash in Transport.destination_table: - next_hop = Transport.destination_table[packet.destination_hash][1] - remaining_hops = Transport.destination_table[packet.destination_hash][2] - RNS.log("Next hop to destination is "+RNS.prettyhexrep(next_hop)+" with "+str(remaining_hops)+" hops remaining, transporting it.", RNS.LOG_DEBUG) - if remaining_hops > 1: - # Just increase hop count and transmit - new_raw = packet.raw[0:1] - new_raw += struct.pack("!B", packet.hops) - new_raw += next_hop - new_raw += packet.raw[12:] - else: - # Strip transport headers and transmit - new_flags = (RNS.Packet.HEADER_1) << 6 | (Transport.BROADCAST) << 4 | (packet.flags & 0b00001111) - new_raw = struct.pack("!B", new_flags) - new_raw += struct.pack("!B", packet.hops) - new_raw += packet.raw[12:] + if RNS.Reticulum.transport_enabled(): + if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE: + if packet.transport_id == Transport.identity.hash: + RNS.log("Received packet in transport for "+RNS.prettyhexrep(packet.destination_hash)+" with matching transport ID, transporting it...", RNS.LOG_DEBUG) + if packet.destination_hash in Transport.destination_table: + next_hop = Transport.destination_table[packet.destination_hash][1] + remaining_hops = Transport.destination_table[packet.destination_hash][2] + RNS.log("Next hop to destination is "+RNS.prettyhexrep(next_hop)+" with "+str(remaining_hops)+" hops remaining, transporting it.", RNS.LOG_DEBUG) + if remaining_hops > 1: + # Just increase hop count and transmit + new_raw = packet.raw[0:1] + new_raw += struct.pack("!B", packet.hops) + new_raw += next_hop + new_raw += packet.raw[12:] + else: + # Strip transport headers and transmit + new_flags = (RNS.Packet.HEADER_1) << 6 | (Transport.BROADCAST) << 4 | (packet.flags & 0b00001111) + new_raw = struct.pack("!B", new_flags) + new_raw += struct.pack("!B", packet.hops) + new_raw += packet.raw[12:] - outbound_interface = Transport.destination_table[packet.destination_hash][5] - outbound_interface.processOutgoing(new_raw) - Transport.destination_table[packet.destination_hash][0] = time.time() + outbound_interface = Transport.destination_table[packet.destination_hash][5] + outbound_interface.processOutgoing(new_raw) + Transport.destination_table[packet.destination_hash][0] = time.time() - if packet.packet_type == RNS.Packet.LINKREQUEST: - # Entry format is - link_entry = [ time.time(), # 0: Timestamp, - next_hop, # 1: Next-hop transport ID - outbound_interface, # 2: Next-hop interface - remaining_hops, # 3: Remaining hops - packet.receiving_interface, # 4: Received on interface - packet.hops, # 5: Taken hops - packet.destination_hash, # 6: Original destination hash - False] # 7: Validated + if packet.packet_type == RNS.Packet.LINKREQUEST: + # Entry format is + link_entry = [ time.time(), # 0: Timestamp, + next_hop, # 1: Next-hop transport ID + outbound_interface, # 2: Next-hop interface + remaining_hops, # 3: Remaining hops + packet.receiving_interface, # 4: Received on interface + packet.hops, # 5: Taken hops + packet.destination_hash, # 6: Original destination hash + False] # 7: Validated - Transport.link_table[packet.getTruncatedHash()] = link_entry + Transport.link_table[packet.getTruncatedHash()] = link_entry + + else: + # Entry format is + reverse_entry = [ packet.receiving_interface, # 0: Received on interface + outbound_interface, # 1: Outbound interface + time.time()] # 2: Timestamp + + Transport.reverse_table[packet.getTruncatedHash()] = reverse_entry else: - # Entry format is - reverse_entry = [ packet.receiving_interface, # 0: Received on interface - outbound_interface, # 1: Outbound interface - time.time()] # 2: Timestamp - - Transport.reverse_table[packet.getTruncatedHash()] = reverse_entry - - else: - # TODO: There should probably be some kind of REJECT - # mechanism here, to signal to the source that their - # expected path failed - RNS.log("Got packet in transport, but no known path to final destination. Dropping packet.", RNS.LOG_DEBUG) - else: - pass - - # Link transport handling. Directs packetes according - # to entries in the link tables - if packet.packet_type != RNS.Packet.ANNOUNCE and packet.packet_type != RNS.Packet.LINKREQUEST: - if packet.destination_hash in Transport.link_table: - link_entry = Transport.link_table[packet.destination_hash] - # If receiving and outbound interface is - # the same for this link, direction doesn't - # matter, and we simply send the packet on. - outbound_interface = None - if link_entry[2] == link_entry[4]: - # But check that taken hops matches one - # of the expectede values. - if packet.hops == link_entry[3] or packet.hops == link_entry[5]: - outbound_interface = link_entry[2] - else: - # If interfaces differ, we transmit on - # the opposite interface of what the - # packet was received on. - if packet.receiving_interface == link_entry[2]: - # Also check that expected hop count matches - if packet.hops == link_entry[3]: - outbound_interface = link_entry[4] - elif packet.receiving_interface == link_entry[4]: - # Also check that expected hop count matches - if packet.hops == link_entry[5]: - outbound_interface = link_entry[2] - - if outbound_interface != None: - new_raw = packet.raw[0:1] - new_raw += struct.pack("!B", packet.hops) - new_raw += packet.raw[2:] - outbound_interface.processOutgoing(new_raw) - Transport.link_table[packet.destination_hash][0] = time.time() + # TODO: There should probably be some kind of REJECT + # mechanism here, to signal to the source that their + # expected path failed + RNS.log("Got packet in transport, but no known path to final destination. Dropping packet.", RNS.LOG_DEBUG) else: pass + # Link transport handling. Directs packets according + # to entries in the link tables + if packet.packet_type != RNS.Packet.ANNOUNCE and packet.packet_type != RNS.Packet.LINKREQUEST: + if packet.destination_hash in Transport.link_table: + link_entry = Transport.link_table[packet.destination_hash] + # If receiving and outbound interface is + # the same for this link, direction doesn't + # matter, and we simply send the packet on. + outbound_interface = None + if link_entry[2] == link_entry[4]: + # But check that taken hops matches one + # of the expectede values. + if packet.hops == link_entry[3] or packet.hops == link_entry[5]: + outbound_interface = link_entry[2] + else: + # If interfaces differ, we transmit on + # the opposite interface of what the + # packet was received on. + if packet.receiving_interface == link_entry[2]: + # Also check that expected hop count matches + if packet.hops == link_entry[3]: + outbound_interface = link_entry[4] + elif packet.receiving_interface == link_entry[4]: + # Also check that expected hop count matches + if packet.hops == link_entry[5]: + outbound_interface = link_entry[2] + + if outbound_interface != None: + new_raw = packet.raw[0:1] + new_raw += struct.pack("!B", packet.hops) + new_raw += packet.raw[2:] + outbound_interface.processOutgoing(new_raw) + Transport.link_table[packet.destination_hash][0] = time.time() + else: + pass + # Announce handling. Handles logic related to incoming # announces, queueing rebroadcasts of these, and removal @@ -397,7 +401,7 @@ class Transport: # Check if this is a next retransmission from # another node. If it is, we're removing the # announce in question from our pending table - if packet.destination_hash in Transport.announce_table: + if RNS.Reticulum.transport_enabled() and packet.destination_hash in Transport.announce_table: announce_entry = Transport.announce_table[packet.destination_hash] if packet.hops-1 == announce_entry[4]: @@ -472,7 +476,7 @@ class Transport: random_blobs.append(random_blob) retransmit_timeout = now + math.pow(Transport.PATHFINDER_C, packet.hops) + (RNS.rand() * Transport.PATHFINDER_RW) - if packet.context != RNS.Packet.PATH_RESPONSE: + if RNS.Reticulum.transport_enabled() and packet.context != RNS.Packet.PATH_RESPONSE: Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, packet.hops, packet, local_rebroadcasts, block_rebroadcasts] Transport.destination_table[packet.destination_hash] = [now, received_from, packet.hops, expires, random_blobs, packet.receiving_interface, packet] @@ -509,7 +513,7 @@ class Transport: # This is a link request proof, check if it # needs to be transported - if packet.destination_hash in Transport.link_table: + if RNS.Reticulum.transport_enabled() and packet.destination_hash in Transport.link_table: link_entry = Transport.link_table[packet.destination_hash] if packet.receiving_interface == link_entry[2]: # TODO: Should we validate the LR proof at each transport @@ -538,7 +542,6 @@ class Transport: for link in Transport.active_links: if link.link_id == packet.destination_hash: packet.link = link - # plaintext = link.decrypt(packet.data) if len(packet.data) == RNS.PacketReceipt.EXPL_LENGTH: proof_hash = packet.data[:RNS.Identity.HASHLENGTH//8] @@ -546,7 +549,7 @@ class Transport: proof_hash = None # Check if this proof neds to be transported - if packet.destination_hash in Transport.reverse_table: + if RNS.Reticulum.transport_enabled() and packet.destination_hash in Transport.reverse_table: reverse_entry = Transport.reverse_table.pop(packet.destination_hash) if packet.receiving_interface == reverse_entry[1]: RNS.log("Proof received on correct interface, transporting it via "+str(reverse_entry[0]), RNS.LOG_DEBUG)