Implemented unknown path discovery
This commit is contained in:
parent
81b92ffdc1
commit
f3ff71d9b8
@ -40,8 +40,8 @@ class Interface:
|
|||||||
MODE_GATEWAY = 0x06
|
MODE_GATEWAY = 0x06
|
||||||
|
|
||||||
# Which interface modes a Transport Node
|
# Which interface modes a Transport Node
|
||||||
# must act as path request agent for.
|
# should actively discover paths for.
|
||||||
PATH_REQUEST_AGENT_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.rxb = 0
|
self.rxb = 0
|
||||||
|
@ -64,8 +64,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.35 # Grace time before a path announcement is made, allows directly reachable peers to respond first
|
PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds
|
||||||
PATH_REQUEST_RW = 2 # Path request random window
|
PATH_REQUEST_GRACE = 0.35 # Grace time before a path announcement is made, allows directly reachable peers to respond first
|
||||||
|
PATH_REQUEST_RW = 2 # Path request random window
|
||||||
|
|
||||||
LINK_TIMEOUT = RNS.Link.KEEPALIVE * 2
|
LINK_TIMEOUT = RNS.Link.KEEPALIVE * 2
|
||||||
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after max 30 minutes
|
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after max 30 minutes
|
||||||
@ -93,6 +94,8 @@ class Transport:
|
|||||||
tunnels = {} # A table storing tunnels to other transport instances
|
tunnels = {} # A table storing tunnels to other transport instances
|
||||||
announce_rate_table = {} # A table for keeping track of announce rates
|
announce_rate_table = {} # A table for keeping track of announce rates
|
||||||
|
|
||||||
|
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
||||||
|
|
||||||
# Transport control destinations are used
|
# Transport control destinations are used
|
||||||
# for control purposes like path requests
|
# for control purposes like path requests
|
||||||
control_destinations = []
|
control_destinations = []
|
||||||
@ -279,6 +282,10 @@ class Transport:
|
|||||||
def jobs():
|
def jobs():
|
||||||
outgoing = []
|
outgoing = []
|
||||||
Transport.jobs_running = True
|
Transport.jobs_running = True
|
||||||
|
|
||||||
|
# TODO: Remove at some point
|
||||||
|
# start_time = time.time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not Transport.jobs_locked:
|
if not Transport.jobs_locked:
|
||||||
# Process receipts list for timed-out packets
|
# Process receipts list for timed-out packets
|
||||||
@ -384,6 +391,15 @@ class Transport:
|
|||||||
stale_paths.append(destination_hash)
|
stale_paths.append(destination_hash)
|
||||||
RNS.log("Path to "+RNS.prettyhexrep(destination_hash)+" was removed since the attached interface no longer exists", RNS.LOG_DEBUG)
|
RNS.log("Path to "+RNS.prettyhexrep(destination_hash)+" was removed since the attached interface no longer exists", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
# Cull the pending discovery path requests table
|
||||||
|
stale_discovery_path_requests = []
|
||||||
|
for destination_hash in Transport.discovery_path_requests:
|
||||||
|
entry = Transport.discovery_path_requests[destination_hash]
|
||||||
|
|
||||||
|
if time.time() > entry["timeout"]:
|
||||||
|
stale_discovery_path_requests.append(destination_hash)
|
||||||
|
RNS.log("Waiting path request for "+RNS.prettyhexrep(destination_hash)+" timed out and was removed", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
# Cull the tunnel table
|
# Cull the tunnel table
|
||||||
stale_tunnels = []
|
stale_tunnels = []
|
||||||
ti = 0
|
ti = 0
|
||||||
@ -452,6 +468,17 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
RNS.log("Removed "+str(i)+" paths", RNS.LOG_EXTREME)
|
RNS.log("Removed "+str(i)+" paths", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for destination_hash in stale_discovery_path_requests:
|
||||||
|
Transport.discovery_path_requests.pop(destination_hash)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i > 0:
|
||||||
|
if i == 1:
|
||||||
|
RNS.log("Removed "+str(i)+" waiting path request", RNS.LOG_EXTREME)
|
||||||
|
else:
|
||||||
|
RNS.log("Removed "+str(i)+" waiting path requests", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for tunnel_id in stale_tunnels:
|
for tunnel_id in stale_tunnels:
|
||||||
Transport.tunnels.pop(tunnel_id)
|
Transport.tunnels.pop(tunnel_id)
|
||||||
@ -472,6 +499,13 @@ class Transport:
|
|||||||
|
|
||||||
Transport.jobs_running = False
|
Transport.jobs_running = False
|
||||||
|
|
||||||
|
# TODO: Remove at some point
|
||||||
|
# end_time = time.time()
|
||||||
|
# if RNS.loglevel >= RNS.LOG_EXTREME:
|
||||||
|
# duration = round((end_time - start_time) * 1000, 2)
|
||||||
|
# if duration > 1:
|
||||||
|
# RNS.log("Transport jobs took "+str(duration)+"ms", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
for packet in outgoing:
|
for packet in outgoing:
|
||||||
packet.send()
|
packet.send()
|
||||||
|
|
||||||
@ -1207,6 +1241,7 @@ class Transport:
|
|||||||
announce_context = RNS.Packet.NONE
|
announce_context = RNS.Packet.NONE
|
||||||
announce_data = packet.data
|
announce_data = packet.data
|
||||||
|
|
||||||
|
# TODO: Shouldn't the context be PATH_RESPONSE in the first case here?
|
||||||
if Transport.from_local_client(packet) and packet.context == RNS.Packet.PATH_RESPONSE:
|
if Transport.from_local_client(packet) and packet.context == RNS.Packet.PATH_RESPONSE:
|
||||||
for local_interface in Transport.local_client_interfaces:
|
for local_interface in Transport.local_client_interfaces:
|
||||||
if packet.receiving_interface != local_interface:
|
if packet.receiving_interface != local_interface:
|
||||||
@ -1241,6 +1276,38 @@ class Transport:
|
|||||||
new_announce.hops = packet.hops
|
new_announce.hops = packet.hops
|
||||||
new_announce.send()
|
new_announce.send()
|
||||||
|
|
||||||
|
# If we have any waiting discovery path requests
|
||||||
|
# for this destination, we retransmit to that
|
||||||
|
# interface immediately
|
||||||
|
if packet.destination_hash in Transport.discovery_path_requests:
|
||||||
|
pr_entry = Transport.discovery_path_requests[packet.destination_hash]
|
||||||
|
attached_interface = pr_entry["requesting_interface"]
|
||||||
|
|
||||||
|
interface_str = " on "+str(attached_interface)
|
||||||
|
|
||||||
|
RNS.log("Got matching announce, answering waiting discovery path request for "+RNS.prettyhexrep(packet.destination_hash)+interface_str, RNS.LOG_DEBUG)
|
||||||
|
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()
|
||||||
|
announce_context = RNS.Packet.NONE
|
||||||
|
announce_data = packet.data
|
||||||
|
|
||||||
|
new_announce = RNS.Packet(
|
||||||
|
announce_destination,
|
||||||
|
announce_data,
|
||||||
|
RNS.Packet.ANNOUNCE,
|
||||||
|
context = RNS.Packet.PATH_RESPONSE,
|
||||||
|
header_type = RNS.Packet.HEADER_2,
|
||||||
|
transport_type = Transport.TRANSPORT,
|
||||||
|
transport_id = Transport.identity.hash,
|
||||||
|
attached_interface = attached_interface
|
||||||
|
)
|
||||||
|
|
||||||
|
new_announce.hops = packet.hops
|
||||||
|
new_announce.send()
|
||||||
|
|
||||||
|
|
||||||
destination_table_entry = [now, received_from, announce_hops, expires, random_blobs, packet.receiving_interface, packet]
|
destination_table_entry = [now, received_from, announce_hops, expires, random_blobs, packet.receiving_interface, packet]
|
||||||
Transport.destination_table[packet.destination_hash] = destination_table_entry
|
Transport.destination_table[packet.destination_hash] = destination_table_entry
|
||||||
RNS.log("Destination "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
|
RNS.log("Destination "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
|
||||||
@ -1323,7 +1390,7 @@ class Transport:
|
|||||||
if packet.receiving_interface == link_entry[2]:
|
if packet.receiving_interface == link_entry[2]:
|
||||||
# TODO: Should we validate the LR proof at each transport
|
# TODO: Should we validate the LR proof at each transport
|
||||||
# step before transporting it?
|
# step before transporting it?
|
||||||
RNS.log("Link request proof received on correct interface, transporting it via "+str(link_entry[4]), RNS.LOG_DEBUG)
|
# RNS.log("Link request proof received on correct interface, transporting it via "+str(link_entry[4]), RNS.LOG_EXTREME)
|
||||||
new_raw = packet.raw[0:1]
|
new_raw = packet.raw[0:1]
|
||||||
new_raw += struct.pack("!B", packet.hops)
|
new_raw += struct.pack("!B", packet.hops)
|
||||||
new_raw += packet.raw[2:]
|
new_raw += packet.raw[2:]
|
||||||
@ -1730,13 +1797,17 @@ class Transport:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None):
|
def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None):
|
||||||
|
should_search_for_unknown = False
|
||||||
|
|
||||||
if attached_interface != None:
|
if attached_interface != None:
|
||||||
|
if RNS.Reticulum.transport_enabled() and attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR:
|
||||||
|
should_search_for_unknown = True
|
||||||
|
|
||||||
interface_str = " on "+str(attached_interface)
|
interface_str = " on "+str(attached_interface)
|
||||||
else:
|
else:
|
||||||
interface_str = ""
|
interface_str = ""
|
||||||
|
|
||||||
# TODO: Clean
|
RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG)
|
||||||
# RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG)
|
|
||||||
|
|
||||||
destination_exists_on_local_client = False
|
destination_exists_on_local_client = False
|
||||||
if len(Transport.local_client_interfaces) > 0:
|
if len(Transport.local_client_interfaces) > 0:
|
||||||
@ -1764,7 +1835,7 @@ class Transport:
|
|||||||
# path requests with transport instance keys is quite
|
# path requests with transport instance keys is quite
|
||||||
# inefficient. There is probably a better way. Doing
|
# inefficient. There is probably a better way. Doing
|
||||||
# path invalidation here would decrease the network
|
# path invalidation here would decrease the network
|
||||||
# convergence time.
|
# convergence time. Maybe just drop it?
|
||||||
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG)
|
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG)
|
||||||
else:
|
else:
|
||||||
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG)
|
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG)
|
||||||
@ -1801,6 +1872,20 @@ class Transport:
|
|||||||
if not interface == attached_interface:
|
if not interface == attached_interface:
|
||||||
Transport.request_path(destination_hash, interface)
|
Transport.request_path(destination_hash, interface)
|
||||||
|
|
||||||
|
elif should_search_for_unknown:
|
||||||
|
if destination_hash in Transport.discovery_path_requests:
|
||||||
|
RNS.log("There is already a waiting path request for "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG)
|
||||||
|
else:
|
||||||
|
# Forward path request on all interfaces
|
||||||
|
# except the requestor interface
|
||||||
|
RNS.log("Attempting to discover unknown path to "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG)
|
||||||
|
pr_entry = { "destination_hash": destination_hash, "timeout": time.time()+Transport.PATH_REQUEST_TIMEOUT, "requesting_interface": attached_interface }
|
||||||
|
Transport.discovery_path_requests[destination_hash] = pr_entry
|
||||||
|
|
||||||
|
for interface in Transport.interfaces:
|
||||||
|
if not interface == attached_interface:
|
||||||
|
Transport.request_path(destination_hash, interface)
|
||||||
|
|
||||||
elif not is_from_local_client and len(Transport.local_client_interfaces) > 0:
|
elif not is_from_local_client and len(Transport.local_client_interfaces) > 0:
|
||||||
# Forward the path request on all local
|
# Forward the path request on all local
|
||||||
# client interfaces
|
# client interfaces
|
||||||
|
@ -257,7 +257,7 @@ def main():
|
|||||||
metavar="seconds",
|
metavar="seconds",
|
||||||
type=float,
|
type=float,
|
||||||
help="timeout before giving up",
|
help="timeout before giving up",
|
||||||
default=15
|
default=RNS.Transport.PATH_REQUEST_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
Loading…
Reference in New Issue
Block a user