mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-22 21:50:18 +00:00
Implemented transport for links
This commit is contained in:
parent
67b13bca25
commit
d754ed989c
@ -96,7 +96,7 @@ def client(destination_hexhash, configpath, timeout=None):
|
|||||||
|
|
||||||
# We override the loglevel to provide feedback when
|
# We override the loglevel to provide feedback when
|
||||||
# an announce is received
|
# an announce is received
|
||||||
RNS.loglevel = RNS.LOG_DEBUG
|
RNS.loglevel = RNS.LOG_INFO
|
||||||
|
|
||||||
# Tell the user that the client is ready!
|
# 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)")
|
RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)")
|
||||||
|
@ -180,15 +180,16 @@ def client(destination_hexhash, configpath):
|
|||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Check if we already know the destination
|
|
||||||
server_identity = RNS.Identity.recall(destination_hash)
|
|
||||||
|
|
||||||
# If not, we'll have to wait until an announce arrives
|
# Check if we know a path to the destination
|
||||||
if server_identity == None:
|
if not RNS.Transport.hasPath(destination_hash):
|
||||||
RNS.log("Destination is not yet known, waiting for an announce to arrive... (Ctrl-C to cancel)")
|
RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...")
|
||||||
while (server_identity == None):
|
RNS.Transport.requestPath(destination_hash)
|
||||||
|
while not RNS.Transport.hasPath(destination_hash):
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
server_identity = RNS.Identity.recall(destination_hash)
|
|
||||||
|
# Recall the server identity
|
||||||
|
server_identity = RNS.Identity.recall(destination_hash)
|
||||||
|
|
||||||
# Inform the user that we'll begin connecting
|
# Inform the user that we'll begin connecting
|
||||||
RNS.log("Establishing link with server...")
|
RNS.log("Establishing link with server...")
|
||||||
|
@ -32,7 +32,7 @@ proof 11
|
|||||||
|
|
||||||
+- Header example -+
|
+- Header example -+
|
||||||
|
|
||||||
01010000 00000100
|
01010000 00000100 [ADDR 1, 10 bytes] [ADDR 2, 10 bytes] [CONTEXT]
|
||||||
| | | | |
|
| | | | |
|
||||||
| | | | +-- Context = RESOURCE_HMU
|
| | | | +-- Context = RESOURCE_HMU
|
||||||
| | | +------- DATA packet
|
| | | +------- DATA packet
|
||||||
|
@ -36,15 +36,15 @@ class Identity:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def recall(destination_hash):
|
def recall(destination_hash):
|
||||||
RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_DEBUG)
|
RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_EXTREME)
|
||||||
if destination_hash in Identity.known_destinations:
|
if destination_hash in Identity.known_destinations:
|
||||||
identity_data = Identity.known_destinations[destination_hash]
|
identity_data = Identity.known_destinations[destination_hash]
|
||||||
identity = Identity(public_only=True)
|
identity = Identity(public_only=True)
|
||||||
identity.loadPublicKey(identity_data[2])
|
identity.loadPublicKey(identity_data[2])
|
||||||
RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_DEBUG)
|
RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_EXTREME)
|
||||||
return identity
|
return identity
|
||||||
else:
|
else:
|
||||||
RNS.log("Could not find "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_DEBUG)
|
RNS.log("Could not find "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_EXTREME)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -104,7 +104,7 @@ class Identity:
|
|||||||
announced_identity.loadPublicKey(public_key)
|
announced_identity.loadPublicKey(public_key)
|
||||||
|
|
||||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||||
RNS.Identity.remember(RNS.Identity.fullHash(packet.raw), destination_hash, public_key)
|
RNS.Identity.remember(packet.getHash(), destination_hash, public_key)
|
||||||
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_INFO)
|
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_INFO)
|
||||||
del announced_identity
|
del announced_identity
|
||||||
return True
|
return True
|
||||||
|
@ -144,7 +144,7 @@ class Link:
|
|||||||
self.peer_pub.curve = Link.CURVE
|
self.peer_pub.curve = Link.CURVE
|
||||||
|
|
||||||
def setLinkID(self, packet):
|
def setLinkID(self, packet):
|
||||||
self.link_id = RNS.Identity.truncatedHash(packet.raw)
|
self.link_id = packet.getTruncatedHash()
|
||||||
self.hash = self.link_id
|
self.hash = self.link_id
|
||||||
|
|
||||||
def handshake(self):
|
def handshake(self):
|
||||||
@ -218,7 +218,7 @@ class Link:
|
|||||||
rtt = umsgpack.unpackb(plaintext)
|
rtt = umsgpack.unpackb(plaintext)
|
||||||
self.rtt = max(measured_rtt, rtt)
|
self.rtt = max(measured_rtt, rtt)
|
||||||
self.status = Link.ACTIVE
|
self.status = Link.ACTIVE
|
||||||
# TODO: Link established callback moved here, ok?
|
|
||||||
if self.owner.callbacks.link_established != None:
|
if self.owner.callbacks.link_established != None:
|
||||||
self.owner.callbacks.link_established(self)
|
self.owner.callbacks.link_established(self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -133,8 +133,6 @@ class Packet:
|
|||||||
raise IOError("Packet with header type 2 must have a transport ID")
|
raise IOError("Packet with header type 2 must have a transport ID")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.header += chr(self.context)
|
self.header += chr(self.context)
|
||||||
|
|
||||||
self.raw = self.header + self.ciphertext
|
self.raw = self.header + self.ciphertext
|
||||||
@ -225,6 +223,9 @@ class Packet:
|
|||||||
def getHash(self):
|
def getHash(self):
|
||||||
return RNS.Identity.fullHash(self.getHashablePart())
|
return RNS.Identity.fullHash(self.getHashablePart())
|
||||||
|
|
||||||
|
def getTruncatedHash(self):
|
||||||
|
return RNS.Identity.truncatedHash(self.getHashablePart())
|
||||||
|
|
||||||
def getHashablePart(self):
|
def getHashablePart(self):
|
||||||
hashable_part = struct.pack("!B", struct.unpack("!B", self.raw[0])[0] & 0b00001111)
|
hashable_part = struct.pack("!B", struct.unpack("!B", self.raw[0])[0] & 0b00001111)
|
||||||
if self.header_type == Packet.HEADER_2:
|
if self.header_type == Packet.HEADER_2:
|
||||||
|
102
RNS/Transport.py
102
RNS/Transport.py
@ -48,6 +48,7 @@ class Transport:
|
|||||||
announce_table = {} # A table for storing announces currently waiting to be retransmitted
|
announce_table = {} # A table for storing announces currently waiting to be retransmitted
|
||||||
destination_table = {} # A lookup table containing the next hop to a given destination
|
destination_table = {} # A lookup table containing the next hop to a given destination
|
||||||
reverse_table = {} # A lookup table for storing packet hashes used to return proofs and replies
|
reverse_table = {} # A lookup table for storing packet hashes used to return proofs and replies
|
||||||
|
link_table = {} # A lookup table containing hops for links
|
||||||
|
|
||||||
jobs_locked = False
|
jobs_locked = False
|
||||||
jobs_running = False
|
jobs_running = False
|
||||||
@ -132,8 +133,6 @@ class Transport:
|
|||||||
block_rebroadcasts = announce_entry[7]
|
block_rebroadcasts = announce_entry[7]
|
||||||
announce_context = RNS.Packet.NONE
|
announce_context = RNS.Packet.NONE
|
||||||
if block_rebroadcasts:
|
if block_rebroadcasts:
|
||||||
# TODO: Remove
|
|
||||||
RNS.log("Rebroadcasts blocked, setting context", RNS.LOG_DEBUG)
|
|
||||||
announce_context = RNS.Packet.PATH_RESPONSE
|
announce_context = RNS.Packet.PATH_RESPONSE
|
||||||
announce_data = packet.data
|
announce_data = packet.data
|
||||||
announce_identity = RNS.Identity.recall(packet.destination_hash)
|
announce_identity = RNS.Identity.recall(packet.destination_hash)
|
||||||
@ -205,7 +204,8 @@ class Transport:
|
|||||||
sent = True
|
sent = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Broadcast packet on all outgoing interfaces
|
# Broadcast packet on all outgoing interfaces, or relevant
|
||||||
|
# interface, if packet is for a link
|
||||||
for interface in Transport.interfaces:
|
for interface in Transport.interfaces:
|
||||||
if interface.OUT:
|
if interface.OUT:
|
||||||
should_transmit = True
|
should_transmit = True
|
||||||
@ -214,6 +214,8 @@ class Transport:
|
|||||||
should_transmit = False
|
should_transmit = False
|
||||||
if interface != packet.destination.attached_interface:
|
if interface != packet.destination.attached_interface:
|
||||||
should_transmit = False
|
should_transmit = False
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
if should_transmit:
|
if should_transmit:
|
||||||
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(interface), RNS.LOG_EXTREME)
|
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(interface), RNS.LOG_EXTREME)
|
||||||
@ -249,7 +251,6 @@ class Transport:
|
|||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# TODO: Probably changee to LOG_EXTREME
|
|
||||||
RNS.log("Filtered packet with hash "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_DEBUG)
|
RNS.log("Filtered packet with hash "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_DEBUG)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -271,6 +272,9 @@ class Transport:
|
|||||||
if Transport.packet_filter(packet):
|
if Transport.packet_filter(packet):
|
||||||
Transport.packet_hashlist.append(packet.packet_hash)
|
Transport.packet_hashlist.append(packet.packet_hash)
|
||||||
|
|
||||||
|
# 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 != None and packet.packet_type != RNS.Packet.ANNOUNCE:
|
||||||
if packet.transport_id == Transport.identity.hash:
|
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)
|
RNS.log("Received packet in transport for "+RNS.prettyhexrep(packet.destination_hash)+" with matching transport ID, transporting it...", RNS.LOG_DEBUG)
|
||||||
@ -294,7 +298,27 @@ class Transport:
|
|||||||
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
||||||
outbound_interface.processOutgoing(new_raw)
|
outbound_interface.processOutgoing(new_raw)
|
||||||
|
|
||||||
Transport.reverse_table[packet.packet_hash[:10]] = [packet.receiving_interface, outbound_interface, 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
|
||||||
|
|
||||||
|
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:
|
else:
|
||||||
# TODO: There should probably be some kind of REJECT
|
# TODO: There should probably be some kind of REJECT
|
||||||
# mechanism here, to signal to the source that their
|
# mechanism here, to signal to the source that their
|
||||||
@ -303,9 +327,45 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
pass
|
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)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Announce handling. Handles logic related to incoming
|
# Announce handling. Handles logic related to incoming
|
||||||
# announces, queueing rebroadcasts of these, and removal
|
# announces, queueing rebroadcasts of these, and removal
|
||||||
# of queued announce rebroadcasts once handed to the next node
|
# of queued announce rebroadcasts once handed to the next node.
|
||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
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.validateAnnounce(packet):
|
if local_destination == None and RNS.Identity.validateAnnounce(packet):
|
||||||
@ -427,11 +487,29 @@ class Transport:
|
|||||||
|
|
||||||
elif packet.packet_type == RNS.Packet.PROOF:
|
elif packet.packet_type == RNS.Packet.PROOF:
|
||||||
if packet.context == RNS.Packet.LRPROOF:
|
if packet.context == RNS.Packet.LRPROOF:
|
||||||
# This is a link request proof, forward
|
# This is a link request proof, check if it
|
||||||
# to a waiting link request
|
# needs to be transported
|
||||||
for link in Transport.pending_links:
|
|
||||||
if link.link_id == packet.destination_hash:
|
if packet.destination_hash in Transport.link_table:
|
||||||
link.validateProof(packet)
|
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
|
||||||
|
# step before transporting it?
|
||||||
|
RNS.log("Link request proof received on correct interface, transporting it via "+str(link_entry[4]), RNS.LOG_DEBUG)
|
||||||
|
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
|
||||||
|
link_entry[4].processOutgoing(new_raw)
|
||||||
|
else:
|
||||||
|
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||||
|
else:
|
||||||
|
# Check if we can deliver it to a local
|
||||||
|
# pending link
|
||||||
|
for link in Transport.pending_links:
|
||||||
|
if link.link_id == packet.destination_hash:
|
||||||
|
link.validateProof(packet)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.RESOURCE_PRF:
|
elif packet.context == RNS.Packet.RESOURCE_PRF:
|
||||||
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:
|
||||||
@ -443,8 +521,6 @@ class Transport:
|
|||||||
packet.link = link
|
packet.link = link
|
||||||
# plaintext = link.decrypt(packet.data)
|
# plaintext = link.decrypt(packet.data)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Make sure everything uses new proof handling
|
|
||||||
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:
|
else:
|
||||||
|
@ -73,9 +73,6 @@ def rand():
|
|||||||
result = random.random()
|
result = random.random()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def hexprint(data):
|
|
||||||
print(hexrep(hexrep))
|
|
||||||
|
|
||||||
def hexrep(data, delimit=True):
|
def hexrep(data, delimit=True):
|
||||||
delimiter = ":"
|
delimiter = ":"
|
||||||
if not delimit:
|
if not delimit:
|
||||||
|
Loading…
Reference in New Issue
Block a user