Implemented path requests
This commit is contained in:
parent
09a19aed72
commit
33ce3ef48f
|
@ -96,8 +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
|
||||||
# TODO: Reset this
|
RNS.loglevel = RNS.LOG_INFO
|
||||||
RNS.loglevel = RNS.LOG_DEBUG
|
|
||||||
|
|
||||||
# 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)")
|
||||||
|
@ -108,6 +107,11 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
# command line.
|
# command line.
|
||||||
while True:
|
while True:
|
||||||
raw_input()
|
raw_input()
|
||||||
|
|
||||||
|
# Let's first check if RNS knows a path to the destination.
|
||||||
|
# If it does, we'll load the server identity and create a packet
|
||||||
|
if RNS.Transport.hasPath(destination_hash):
|
||||||
|
|
||||||
# To address the server, we need to know it's public
|
# To address the server, we need to know it's public
|
||||||
# key, so we check if Reticulum knows this destination.
|
# key, so we check if Reticulum knows this destination.
|
||||||
# This is done by calling the "recall" method of the
|
# This is done by calling the "recall" method of the
|
||||||
|
@ -115,7 +119,7 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
# return an Identity instance that can be used in
|
# return an Identity instance that can be used in
|
||||||
# outgoing destinations.
|
# outgoing destinations.
|
||||||
server_identity = RNS.Identity.recall(destination_hash)
|
server_identity = RNS.Identity.recall(destination_hash)
|
||||||
if server_identity != None:
|
|
||||||
# We got the correct identity instance from the
|
# We got the correct identity instance from the
|
||||||
# recall method, so let's create an outgoing
|
# recall method, so let's create an outgoing
|
||||||
# destination. We use the naming convention:
|
# destination. We use the naming convention:
|
||||||
|
@ -152,7 +156,8 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
else:
|
else:
|
||||||
# If we do not know this destination, tell the
|
# If we do not know this destination, tell the
|
||||||
# user to wait for an announce to arrive.
|
# user to wait for an announce to arrive.
|
||||||
RNS.log("Destination is not yet known. Wait for an announce to arrive and try again.")
|
RNS.log("Destination is not yet known. Requesting path...")
|
||||||
|
RNS.Transport.requestPath(destination_hash)
|
||||||
|
|
||||||
# This function is called when our reply destination
|
# This function is called when our reply destination
|
||||||
# receives a proof packet.
|
# receives a proof packet.
|
||||||
|
|
|
@ -28,3 +28,14 @@ data 00
|
||||||
announce 01
|
announce 01
|
||||||
link request 10
|
link request 10
|
||||||
proof 11
|
proof 11
|
||||||
|
|
||||||
|
|
||||||
|
+- Header example -+
|
||||||
|
|
||||||
|
01010000 00000100
|
||||||
|
| | | | |
|
||||||
|
| | | | +-- Context = RESOURCE_HMU
|
||||||
|
| | | +------- DATA packet
|
||||||
|
| | +--------- SINGLE destination
|
||||||
|
| +----------- TRANSPORT propagation type
|
||||||
|
+------------- HEADER_2, two byte header, two address fields
|
|
@ -75,8 +75,8 @@ class Destination:
|
||||||
if identity != None and type == Destination.SINGLE:
|
if identity != None and type == Destination.SINGLE:
|
||||||
aspects = aspects+(identity.hexhash,)
|
aspects = aspects+(identity.hexhash,)
|
||||||
|
|
||||||
if identity == None and direction == Destination.IN:
|
if identity == None and direction == Destination.IN and self.type != Destination.PLAIN:
|
||||||
identity = Identity()
|
identity = RNS.Identity()
|
||||||
aspects = aspects+(identity.hexhash,)
|
aspects = aspects+(identity.hexhash,)
|
||||||
|
|
||||||
self.identity = identity
|
self.identity = identity
|
||||||
|
|
|
@ -57,7 +57,8 @@ class AX25KISSInterface(Interface):
|
||||||
self.timeout = 100
|
self.timeout = 100
|
||||||
self.online = False
|
self.online = False
|
||||||
# TODO: Sane default and make this configurable
|
# TODO: Sane default and make this configurable
|
||||||
self.txdelay = 0.1
|
# TODO: Changed to 1ms instead of 100ms, check it
|
||||||
|
self.txdelay = 0.001
|
||||||
|
|
||||||
self.packet_queue = []
|
self.packet_queue = []
|
||||||
self.flow_control = flow_control
|
self.flow_control = flow_control
|
||||||
|
|
|
@ -29,8 +29,9 @@ class Packet:
|
||||||
CACHE_REQUEST = 0x08 # Packet is a cache request
|
CACHE_REQUEST = 0x08 # Packet is a cache request
|
||||||
REQUEST = 0x09 # Packet is a request
|
REQUEST = 0x09 # Packet is a request
|
||||||
RESPONSE = 0x0A # Packet is a response to a request
|
RESPONSE = 0x0A # Packet is a response to a request
|
||||||
COMMAND = 0x0B # Packet is a command
|
PATH_RESPONSE = 0x0B # Packet is a response to a path request
|
||||||
COMMAND_STATUS = 0x0C # Packet is a status of an executed command
|
COMMAND = 0x0C # Packet is a command
|
||||||
|
COMMAND_STATUS = 0x0D # Packet is a status of an executed command
|
||||||
KEEPALIVE = 0xFB # Packet is a keepalive packet
|
KEEPALIVE = 0xFB # Packet is a keepalive packet
|
||||||
LINKCLOSE = 0xFC # Packet is a link close message
|
LINKCLOSE = 0xFC # Packet is a link close message
|
||||||
LINKPROOF = 0xFD # Packet is a link packet proof
|
LINKPROOF = 0xFD # Packet is a link packet proof
|
||||||
|
@ -330,6 +331,9 @@ class PacketReceipt:
|
||||||
return False
|
return False
|
||||||
elif len(proof) == PacketReceipt.IMPL_LENGTH:
|
elif len(proof) == PacketReceipt.IMPL_LENGTH:
|
||||||
# This is an implicit proof
|
# This is an implicit proof
|
||||||
|
if self.destination.identity == None:
|
||||||
|
return False
|
||||||
|
|
||||||
signature = proof[:RNS.Identity.SIGLENGTH/8]
|
signature = proof[:RNS.Identity.SIGLENGTH/8]
|
||||||
proof_valid = self.destination.identity.validate(signature, self.hash)
|
proof_valid = self.destination.identity.validate(signature, self.hash)
|
||||||
if proof_valid:
|
if proof_valid:
|
||||||
|
|
|
@ -20,11 +20,13 @@ class Transport:
|
||||||
REACHABILITY_DIRECT = 0x01
|
REACHABILITY_DIRECT = 0x01
|
||||||
REACHABILITY_TRANSPORT = 0x02
|
REACHABILITY_TRANSPORT = 0x02
|
||||||
|
|
||||||
|
APP_NAME = "rnstransport"
|
||||||
|
|
||||||
# TODO: Document the addition of random windows
|
# TODO: Document the addition of random windows
|
||||||
# and max local rebroadcasts.
|
# and max local rebroadcasts.
|
||||||
PATHFINDER_M = 18 # Max hops
|
PATHFINDER_M = 18 # Max hops
|
||||||
PATHFINDER_C = 2.0 # Decay constant
|
PATHFINDER_C = 2.0 # Decay constant
|
||||||
PATHFINDER_R = 2 # Retransmit retries
|
PATHFINDER_R = 1 # Retransmit retries
|
||||||
PATHFINDER_T = 10 # Retry grace period
|
PATHFINDER_T = 10 # Retry grace period
|
||||||
PATHFINDER_RW = 10 # Random window for announce rebroadcast
|
PATHFINDER_RW = 10 # Random window for announce rebroadcast
|
||||||
PATHFINDER_E = 60*15 # Path expiration in seconds
|
PATHFINDER_E = 60*15 # Path expiration in seconds
|
||||||
|
@ -33,6 +35,9 @@ class Transport:
|
||||||
# various situations
|
# various situations
|
||||||
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
|
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
|
||||||
|
|
||||||
|
PATH_REQUEST_GRACE = 0.25 # Grace time before a path announcement is made, allows directly reachable peers to respond first
|
||||||
|
PATH_REQUEST_RW = 2 # Path request random window
|
||||||
|
|
||||||
interfaces = [] # All active interfaces
|
interfaces = [] # All active interfaces
|
||||||
destinations = [] # All active destinations
|
destinations = [] # All active destinations
|
||||||
pending_links = [] # Links that are being established
|
pending_links = [] # Links that are being established
|
||||||
|
@ -78,6 +83,9 @@ class Transport:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not load packet hashlist from disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
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)
|
||||||
|
|
||||||
thread = threading.Thread(target=Transport.jobloop)
|
thread = threading.Thread(target=Transport.jobloop)
|
||||||
thread.setDaemon(True)
|
thread.setDaemon(True)
|
||||||
|
@ -121,12 +129,16 @@ class Transport:
|
||||||
announce_entry[1] = time.time() + math.pow(Transport.PATHFINDER_C, announce_entry[4]) + Transport.PATHFINDER_T + Transport.PATHFINDER_RW
|
announce_entry[1] = time.time() + math.pow(Transport.PATHFINDER_C, announce_entry[4]) + Transport.PATHFINDER_T + Transport.PATHFINDER_RW
|
||||||
announce_entry[2] += 1
|
announce_entry[2] += 1
|
||||||
packet = announce_entry[5]
|
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_data = packet.data
|
||||||
announce_identity = RNS.Identity.recall(packet.destination_hash)
|
announce_identity = RNS.Identity.recall(packet.destination_hash)
|
||||||
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
|
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
|
||||||
announce_destination.hash = packet.destination_hash
|
announce_destination.hash = packet.destination_hash
|
||||||
announce_destination.hexhash = announce_destination.hash.encode("hex_codec")
|
announce_destination.hexhash = announce_destination.hash.encode("hex_codec")
|
||||||
new_packet = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE, header_type = RNS.Packet.HEADER_2, transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash)
|
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]
|
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)
|
RNS.log("Rebroadcasting announce for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG)
|
||||||
outgoing.append(new_packet)
|
outgoing.append(new_packet)
|
||||||
|
@ -185,7 +197,6 @@ class Transport:
|
||||||
# Destination is directly reachable, and we know on
|
# Destination is directly reachable, and we know on
|
||||||
# what interface, so transmit only on that one
|
# what interface, so transmit only on that one
|
||||||
|
|
||||||
# TODO: Strip transport headers here
|
|
||||||
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(outbound_interface), RNS.LOG_EXTREME)
|
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(outbound_interface), RNS.LOG_EXTREME)
|
||||||
RNS.log("Hash is "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
|
RNS.log("Hash is "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
|
||||||
outbound_interface.processOutgoing(packet.raw)
|
outbound_interface.processOutgoing(packet.raw)
|
||||||
|
@ -212,7 +223,7 @@ class Transport:
|
||||||
packet.sent = True
|
packet.sent = True
|
||||||
packet.sent_at = time.time()
|
packet.sent_at = time.time()
|
||||||
|
|
||||||
if (packet.packet_type == RNS.Packet.DATA):
|
if (packet.packet_type == RNS.Packet.DATA and packet.destination.type != RNS.Destination.PLAIN):
|
||||||
packet.receipt = RNS.PacketReceipt(packet)
|
packet.receipt = RNS.PacketReceipt(packet)
|
||||||
Transport.receipts.append(packet.receipt)
|
Transport.receipts.append(packet.receipt)
|
||||||
|
|
||||||
|
@ -373,10 +384,13 @@ class Transport:
|
||||||
retries = 0
|
retries = 0
|
||||||
expires = now + Transport.PATHFINDER_E
|
expires = now + Transport.PATHFINDER_E
|
||||||
local_rebroadcasts = 0
|
local_rebroadcasts = 0
|
||||||
|
block_rebroadcasts = False
|
||||||
random_blobs.append(random_blob)
|
random_blobs.append(random_blob)
|
||||||
retransmit_timeout = now + math.pow(Transport.PATHFINDER_C, packet.hops) + (RNS.rand() * Transport.PATHFINDER_RW)
|
retransmit_timeout = now + math.pow(Transport.PATHFINDER_C, packet.hops) + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||||
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, packet.hops, packet, local_rebroadcasts]
|
if packet.context != RNS.Packet.PATH_RESPONSE:
|
||||||
Transport.destination_table[packet.destination_hash] = [now, received_from, packet.hops, expires, random_blobs, packet.receiving_interface]
|
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]
|
||||||
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" is now via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
|
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" is now via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
elif packet.packet_type == RNS.Packet.LINKREQUEST:
|
elif packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||||
|
@ -533,6 +547,55 @@ class Transport:
|
||||||
else:
|
else:
|
||||||
cache_request_packet = RNS.Packet(Transport.transport_destination(), packet_hash, context = RNS.Packet.CACHE_REQUEST)
|
cache_request_packet = RNS.Packet(Transport.transport_destination(), packet_hash, context = RNS.Packet.CACHE_REQUEST)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def hasPath(destination_hash):
|
||||||
|
if destination_hash in Transport.destination_table:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def requestPath(destination_hash):
|
||||||
|
path_request_data = destination_hash + RNS.Identity.getRandomHash()
|
||||||
|
path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||||
|
packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1)
|
||||||
|
packet.send()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pathRequestHandler(data, packet):
|
||||||
|
if len(data) >= RNS.Identity.TRUNCATED_HASHLENGTH/8:
|
||||||
|
Transport.pathRequest(data[:RNS.Identity.TRUNCATED_HASHLENGTH/8])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pathRequest(destination_hash):
|
||||||
|
RNS.log("Path request for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
|
||||||
|
if local_destination != None:
|
||||||
|
RNS.log("Destination is local to this system, announcing", RNS.LOG_DEBUG)
|
||||||
|
local_destination.announce()
|
||||||
|
|
||||||
|
elif destination_hash in Transport.destination_table:
|
||||||
|
RNS.log("Path found, inserting announce for transmission", RNS.LOG_DEBUG)
|
||||||
|
packet = Transport.destination_table[destination_hash][6]
|
||||||
|
received_from = Transport.destination_table[destination_hash][5]
|
||||||
|
|
||||||
|
# Setting hops to 0xFF ensures announce is not rebroadcast by any local
|
||||||
|
# nodes, but requester will still see it and get a valid path
|
||||||
|
# TODO: Consider if there is a more elegant way to do this, or whether
|
||||||
|
# rebroadcasts should actually be allowed here
|
||||||
|
faux_hops = packet.hops
|
||||||
|
now = time.time()
|
||||||
|
retries = Transport.PATHFINDER_R
|
||||||
|
local_rebroadcasts = 0
|
||||||
|
block_rebroadcasts = True
|
||||||
|
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||||
|
|
||||||
|
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, faux_hops, packet, local_rebroadcasts, block_rebroadcasts]
|
||||||
|
|
||||||
|
else:
|
||||||
|
RNS.log("No known path to requested destination, ignoring request", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def transport_destination():
|
def transport_destination():
|
||||||
# TODO: implement this
|
# TODO: implement this
|
||||||
|
|
Loading…
Reference in New Issue