Destination table is now stored on exit

This commit is contained in:
Mark Qvist 2020-05-11 21:05:04 +02:00
parent 5f0f53eba1
commit 49fce1e1cb
3 changed files with 275 additions and 182 deletions

View File

@ -1,3 +1,5 @@
import RNS
class Interface: class Interface:
IN = False IN = False
OUT = False OUT = False
@ -7,3 +9,7 @@ class Interface:
def __init__(self): def __init__(self):
pass pass
def get_hash(self):
# TODO: Maybe expand this to something more unique
return RNS.Identity.fullHash(str(self).encode("utf-8"))

View File

@ -48,8 +48,13 @@ class Reticulum:
os.makedirs(Reticulum.cachepath) os.makedirs(Reticulum.cachepath)
if os.path.isfile(self.configpath): if os.path.isfile(self.configpath):
self.config = ConfigObj(self.configpath) try:
RNS.log("Configuration loaded from "+self.configpath) self.config = ConfigObj(self.configpath)
RNS.log("Configuration loaded from "+self.configpath)
except Exception as e:
RNS.log("Could not parse the configuration at "+self.configpath, RNS.LOG_ERROR)
RNS.log("Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic()
else: else:
RNS.log("Could not load config file, creating default configuration file...") RNS.log("Could not load config file, creating default configuration file...")
self.createDefaultConfig() self.createDefaultConfig()
@ -103,207 +108,211 @@ class Reticulum:
RNS.log("", RNS.LOG_CRITICAL) RNS.log("", RNS.LOG_CRITICAL)
Reticulum.__allow_unencrypted = True Reticulum.__allow_unencrypted = True
interface_names = []
for name in self.config["interfaces"]: for name in self.config["interfaces"]:
c = self.config["interfaces"][name] if not name in interface_names:
c = self.config["interfaces"][name]
try: try:
if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True: if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True:
if c["type"] == "UdpInterface": if c["type"] == "UdpInterface":
interface = UdpInterface.UdpInterface( interface = UdpInterface.UdpInterface(
RNS.Transport, RNS.Transport,
name, name,
c["listen_ip"], c["listen_ip"],
int(c["listen_port"]), int(c["listen_port"]),
c["forward_ip"], c["forward_ip"],
int(c["forward_port"]) int(c["forward_port"])
) )
if "outgoing" in c and c.as_bool("outgoing") == True: if "outgoing" in c and c.as_bool("outgoing") == True:
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
if c["type"] == "TCPServerInterface": if c["type"] == "TCPServerInterface":
interface = TCPInterface.TCPServerInterface( interface = TCPInterface.TCPServerInterface(
RNS.Transport, RNS.Transport,
name, name,
c["listen_ip"], c["listen_ip"],
int(c["listen_port"]) int(c["listen_port"])
) )
if "outgoing" in c and c.as_bool("outgoing") == True: if "outgoing" in c and c.as_bool("outgoing") == True:
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
if c["type"] == "TCPClientInterface": if c["type"] == "TCPClientInterface":
interface = TCPInterface.TCPClientInterface( interface = TCPInterface.TCPClientInterface(
RNS.Transport, RNS.Transport,
name, name,
c["target_host"], c["target_host"],
int(c["target_port"]) int(c["target_port"])
) )
if "outgoing" in c and c.as_bool("outgoing") == True: if "outgoing" in c and c.as_bool("outgoing") == True:
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
if c["type"] == "SerialInterface": if c["type"] == "SerialInterface":
port = c["port"] if "port" in c else None port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600 speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8 databits = int(c["databits"]) if "databits" in c else 8
parity = c["parity"] if "parity" in c else "N" parity = c["parity"] if "parity" in c else "N"
stopbits = int(c["stopbits"]) if "stopbits" in c else 1 stopbits = int(c["stopbits"]) if "stopbits" in c else 1
if port == None: if port == None:
raise ValueError("No port specified for serial interface") raise ValueError("No port specified for serial interface")
interface = SerialInterface.SerialInterface( interface = SerialInterface.SerialInterface(
RNS.Transport, RNS.Transport,
name, name,
port, port,
speed, speed,
databits, databits,
parity, parity,
stopbits stopbits
) )
if "outgoing" in c and c["outgoing"].lower() == "true": if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
if c["type"] == "KISSInterface": if c["type"] == "KISSInterface":
preamble = int(c["preamble"]) if "preamble" in c else None preamble = int(c["preamble"]) if "preamble" in c else None
txtail = int(c["txtail"]) if "txtail" in c else None txtail = int(c["txtail"]) if "txtail" in c else None
persistence = int(c["persistence"]) if "persistence" in c else None persistence = int(c["persistence"]) if "persistence" in c else None
slottime = int(c["slottime"]) if "slottime" in c else None slottime = int(c["slottime"]) if "slottime" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
port = c["port"] if "port" in c else None port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600 speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8 databits = int(c["databits"]) if "databits" in c else 8
parity = c["parity"] if "parity" in c else "N" parity = c["parity"] if "parity" in c else "N"
stopbits = int(c["stopbits"]) if "stopbits" in c else 1 stopbits = int(c["stopbits"]) if "stopbits" in c else 1
if port == None: if port == None:
raise ValueError("No port specified for serial interface") raise ValueError("No port specified for serial interface")
interface = KISSInterface.KISSInterface( interface = KISSInterface.KISSInterface(
RNS.Transport, RNS.Transport,
name, name,
port, port,
speed, speed,
databits, databits,
parity, parity,
stopbits, stopbits,
preamble, preamble,
txtail, txtail,
persistence, persistence,
slottime, slottime,
flow_control flow_control
) )
if "outgoing" in c and c["outgoing"].lower() == "true": if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
if c["type"] == "AX25KISSInterface": if c["type"] == "AX25KISSInterface":
preamble = int(c["preamble"]) if "preamble" in c else None preamble = int(c["preamble"]) if "preamble" in c else None
txtail = int(c["txtail"]) if "txtail" in c else None txtail = int(c["txtail"]) if "txtail" in c else None
persistence = int(c["persistence"]) if "persistence" in c else None persistence = int(c["persistence"]) if "persistence" in c else None
slottime = int(c["slottime"]) if "slottime" in c else None slottime = int(c["slottime"]) if "slottime" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
port = c["port"] if "port" in c else None port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600 speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8 databits = int(c["databits"]) if "databits" in c else 8
parity = c["parity"] if "parity" in c else "N" parity = c["parity"] if "parity" in c else "N"
stopbits = int(c["stopbits"]) if "stopbits" in c else 1 stopbits = int(c["stopbits"]) if "stopbits" in c else 1
callsign = c["callsign"] if "callsign" in c else "" callsign = c["callsign"] if "callsign" in c else ""
ssid = int(c["ssid"]) if "ssid" in c else -1 ssid = int(c["ssid"]) if "ssid" in c else -1
if port == None: if port == None:
raise ValueError("No port specified for serial interface") raise ValueError("No port specified for serial interface")
interface = AX25KISSInterface.AX25KISSInterface( interface = AX25KISSInterface.AX25KISSInterface(
RNS.Transport, RNS.Transport,
name, name,
callsign, callsign,
ssid, ssid,
port, port,
speed, speed,
databits, databits,
parity, parity,
stopbits, stopbits,
preamble, preamble,
txtail, txtail,
persistence, persistence,
slottime, slottime,
flow_control flow_control
) )
if "outgoing" in c and c["outgoing"].lower() == "true": if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
if c["type"] == "RNodeInterface": if c["type"] == "RNodeInterface":
frequency = int(c["frequency"]) if "frequency" in c else None frequency = int(c["frequency"]) if "frequency" in c else None
bandwidth = int(c["bandwidth"]) if "bandwidth" in c else None bandwidth = int(c["bandwidth"]) if "bandwidth" in c else None
txpower = int(c["txpower"]) if "txpower" in c else None txpower = int(c["txpower"]) if "txpower" in c else None
spreadingfactor = int(c["spreadingfactor"]) if "spreadingfactor" in c else None spreadingfactor = int(c["spreadingfactor"]) if "spreadingfactor" in c else None
codingrate = int(c["codingrate"]) if "codingrate" in c else None codingrate = int(c["codingrate"]) if "codingrate" in c else None
flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False
port = c["port"] if "port" in c else None port = c["port"] if "port" in c else None
if port == None: if port == None:
raise ValueError("No port specified for RNode interface") raise ValueError("No port specified for RNode interface")
interface = RNodeInterface.RNodeInterface( interface = RNodeInterface.RNodeInterface(
RNS.Transport, RNS.Transport,
name, name,
port, port,
frequency, frequency,
bandwidth, bandwidth,
txpower, txpower,
spreadingfactor, spreadingfactor,
flow_control flow_control
) )
if "outgoing" in c and c["outgoing"].lower() == "true": if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True interface.OUT = True
else: else:
interface.OUT = False interface.OUT = False
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
else: else:
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_VERBOSE) RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR) RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
raise e RNS.panic()
else:
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic()
def createDefaultConfig(self): def createDefaultConfig(self):

View File

@ -70,25 +70,25 @@ class Transport:
@staticmethod @staticmethod
def start(): def start():
if Transport.identity == None: if Transport.identity == None:
transport_identity_path = RNS.Reticulum.configdir+"/transportidentity" transport_identity_path = RNS.Reticulum.storagepath+"/transport_identity"
if os.path.isfile(transport_identity_path): if os.path.isfile(transport_identity_path):
Transport.identity = RNS.Identity.from_file(transport_identity_path) Transport.identity = RNS.Identity.from_file(transport_identity_path)
if Transport.identity == None: if Transport.identity == None:
RNS.log("No valid Transport Identity on disk, creating...", RNS.LOG_VERBOSE) RNS.log("No valid Transport Identity in storage, creating...", RNS.LOG_VERBOSE)
Transport.identity = RNS.Identity() Transport.identity = RNS.Identity()
Transport.identity.save(transport_identity_path) Transport.identity.save(transport_identity_path)
else: else:
RNS.log("Loaded Transport Identity from disk", RNS.LOG_VERBOSE) RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE)
packet_hashlist_path = RNS.Reticulum.configdir+"/packet_hashlist" packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
if os.path.isfile(packet_hashlist_path): if os.path.isfile(packet_hashlist_path):
try: try:
file = open(packet_hashlist_path, "rb") file = open(packet_hashlist_path, "rb")
Transport.packet_hashlist = umsgpack.unpackb(file.read()) Transport.packet_hashlist = umsgpack.unpackb(file.read())
file.close() file.close()
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 storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
# Create transport-specific destinations # Create transport-specific destinations
path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
@ -99,6 +99,38 @@ class Transport:
thread.start() thread.start()
if RNS.Reticulum.transport_enabled(): if RNS.Reticulum.transport_enabled():
destination_table_path = RNS.Reticulum.storagepath+"/destination_table"
if os.path.isfile(destination_table_path):
serialised_destinations = []
try:
file = open(destination_table_path, "rb")
serialised_destinations = umsgpack.unpackb(file.read())
file.close()
for serialised_entry in serialised_destinations:
destination_hash = serialised_entry[0]
receiving_interface = Transport.find_interface_from_hash(serialised_entry[6])
announce_packet = Transport.get_cached_packet(serialised_entry[7])
if announce_packet != None and receiving_interface != None:
announce_packet.unpack()
timestamp = serialised_entry[1]
received_from = serialised_entry[2]
hops = serialised_entry[3]
expires = serialised_entry[4]
random_blobs = serialised_entry[5]
Transport.destination_table[destination_hash] = [timestamp, received_from, hops, expires, random_blobs, receiving_interface, announce_packet]
RNS.log("Loaded destination table entry for "+RNS.prettyhexrep(destination_hash)+" from storage", RNS.LOG_DEBUG)
else:
RNS.log("Could not reconstruct destination table entry from storage for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
if announce_packet == None:
RNS.log("The announce packet could not be loaded from cache", RNS.LOG_DEBUG)
if receiving_interface == None:
RNS.log("The interface is no longer available", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Could not load destination table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("Transport instance "+str(Transport.identity)+" started") RNS.log("Transport instance "+str(Transport.identity)+" started")
@staticmethod @staticmethod
@ -599,6 +631,13 @@ class Transport:
else: else:
RNS.log("Attempted to activate a link that was not in the pending table", RNS.LOG_ERROR) RNS.log("Attempted to activate a link that was not in the pending table", RNS.LOG_ERROR)
@staticmethod
def find_interface_from_hash(interface_hash):
for interface in Transport.interfaces:
if interface.get_hash() == interface_hash:
return interface
return None
@staticmethod @staticmethod
def shouldCache(packet): def shouldCache(packet):
@ -610,8 +649,8 @@ class Transport:
return False return False
@staticmethod @staticmethod
def cache(packet): def cache(packet, force_cache=False):
if RNS.Transport.shouldCache(packet): if RNS.Transport.shouldCache(packet) or force_cache:
try: try:
packet_hash = RNS.hexrep(packet.getHash(), delimit=False) packet_hash = RNS.hexrep(packet.getHash(), delimit=False)
file = open(RNS.Reticulum.cachepath+"/"+packet_hash, "wb") file = open(RNS.Reticulum.cachepath+"/"+packet_hash, "wb")
@ -622,20 +661,36 @@ class Transport:
RNS.log("Error writing packet to cache", RNS.LOG_ERROR) RNS.log("Error writing packet to cache", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e)) RNS.log("The contained exception was: "+str(e))
# TODO: Implement cache requests. Needs methodology
# rethinking. This is skeleton code.
@staticmethod @staticmethod
def cache_request_packet(packet): def get_cached_packet(packet_hash):
if len(packet.data) == RNS.Identity.HASHLENGTH/8: try:
packet_hash = RNS.hexrep(packet.data, delimit=False) packet_hash = RNS.hexrep(packet_hash, delimit=False)
path = RNS.Reticulum.cachepath+"/"+packet_hash path = RNS.Reticulum.cachepath+"/"+packet_hash
if os.path.isfile(path): if os.path.isfile(path):
file = open(path, "rb") file = open(path, "rb")
raw = file.read() raw = file.read()
file.close() file.close()
packet = RNS.Packet(None, raw) packet = RNS.Packet(None, raw)
# TODO: Implement outbound for this return packet
else:
return None
except Exception as e:
RNS.log("Exception occurred while getting cached packet.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
# TODO: Implement cache requests. Needs methodology
# rethinking. This is skeleton code.
@staticmethod
def cache_request_packet(packet):
if len(packet.data) == RNS.Identity.HASHLENGTH/8:
packet_hash = RNS.hexrep(packet.data, delimit=False)
packet = Transport.get_cached_packet(packet_hash)
if packet != None:
# TODO: Implement outbound for this
pass
else:
pass
# TODO: Implement cache requests. Needs methodology # TODO: Implement cache requests. Needs methodology
# rethinking. This is skeleton code. # rethinking. This is skeleton code.
@ -704,10 +759,33 @@ class Transport:
@staticmethod @staticmethod
def exitHandler(): def exitHandler():
RNS.log("Saving packet hashlist to storage...", RNS.LOG_VERBOSE)
try: try:
packet_hashlist_path = RNS.Reticulum.configdir+"/packet_hashlist" packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
file = open(packet_hashlist_path, "wb") file = open(packet_hashlist_path, "wb")
file.write(umsgpack.packb(Transport.packet_hashlist)) file.write(umsgpack.packb(Transport.packet_hashlist))
file.close() file.close()
RNS.log("Done packet hashlist to storage", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("Could not save packet hashlist to disk, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("Saving destination table to storage...", RNS.LOG_VERBOSE)
try:
serialised_destinations = []
for destination_hash in Transport.destination_table:
destination_entry = Transport.destination_table[destination_hash]
de = destination_entry
Transport.cache(de[6], force_cache=True)
interface_hash = de[5].get_hash()
packet_hash = de[6].getHash()
serialised_entry = [destination_hash, de[0], de[1], de[2], de[3], de[4], interface_hash, packet_hash]
serialised_destinations.append(serialised_entry)
destination_table_path = RNS.Reticulum.storagepath+"/destination_table"
file = open(destination_table_path, "wb")
file.write(umsgpack.packb(serialised_destinations))
file.close()
RNS.log("Done saving destination table to storage", RNS.LOG_VERBOSE)
except Exception as e:
RNS.log("Could not save destination table to storage, the contained exception was: "+str(e), RNS.LOG_ERROR)