Refactored to new naming scheme. Implemented KISS interface.

This commit is contained in:
Mark Qvist 2018-04-04 14:14:22 +02:00
parent cfb1ed84d2
commit 0196c94672
16 changed files with 476 additions and 205 deletions

View File

@ -1,111 +0,0 @@
from Interfaces import *
import ConfigParser
from vendor.configobj import ConfigObj
import atexit
import struct
import array
import os.path
import os
import FPE
class FlexPE:
MTU = 500
router = None
config = None
configdir = os.path.expanduser("~")+"/.flexpe"
configpath = ""
storagepath = ""
cachepath = ""
def __init__(self,configdir=None):
if configdir != None:
FlexPE.configdir = configdir
FlexPE.configpath = FlexPE.configdir+"/config"
FlexPE.storagepath = FlexPE.configdir+"/storage"
FlexPE.cachepath = FlexPE.configdir+"/storage/cache"
if not os.path.isdir(FlexPE.storagepath):
os.makedirs(FlexPE.storagepath)
if not os.path.isdir(FlexPE.cachepath):
os.makedirs(FlexPE.cachepath)
if os.path.isfile(self.configpath):
self.config = ConfigObj(self.configpath)
FPE.log("Configuration loaded from "+self.configpath)
else:
FPE.log("Could not load config file, creating default configuration...")
self.createDefaultConfig()
self.applyConfig()
FPE.Identity.loadKnownDestinations()
FlexPE.router = self
atexit.register(FPE.Identity.exitHandler)
def applyConfig(self):
if "logging" in self.config:
for option in self.config["logging"]:
value = self.config["logging"][option]
if option == "loglevel":
FPE.loglevel = int(value)
for name in self.config["interfaces"]:
c = self.config["interfaces"][name]
try:
if c["type"] == "UdpInterface":
interface = UdpInterface.UdpInterface(
FPE.Transport,
c["listen_ip"],
int(c["listen_port"]),
c["forward_ip"],
int(c["forward_port"])
)
if c["use_as_outgoing"].lower() == "true":
interface.OUT = True
interface.name = name
FPE.Transport.interfaces.append(interface)
if c["type"] == "SerialInterface":
interface = SerialInterface.SerialInterface(
FPE.Transport,
c["port"],
int(c["speed"]),
int(c["databits"]),
c["parity"],
int(c["stopbits"])
)
if c["use_as_outgoing"].lower() == "true":
interface.OUT = True
interface.name = name
FPE.Transport.interfaces.append(interface)
except Exception as e:
FPE.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", FPE.LOG_ERROR)
FPE.log("The contained exception was: "+str(e), FPE.LOG_ERROR)
def createDefaultConfig(self):
self.config = ConfigObj()
self.config.filename = FlexPE.configpath
self.config["interfaces"] = {}
self.config["interfaces"]["Default UDP Interface"] = {}
self.config["interfaces"]["Default UDP Interface"]["type"] = "UdpInterface"
self.config["interfaces"]["Default UDP Interface"]["listen_ip"] = "0.0.0.0"
self.config["interfaces"]["Default UDP Interface"]["listen_port"] = 7777
self.config["interfaces"]["Default UDP Interface"]["forward_ip"] = "255.255.255.255"
self.config["interfaces"]["Default UDP Interface"]["forward_port"] = 7777
self.config["interfaces"]["Default UDP Interface"]["use_as_outgoing"] = "true"
if not os.path.isdir(FlexPE.configdir):
os.makedirs(FlexPE.configdir)
self.config.write()
self.applyConfig()

View File

@ -1,6 +1,6 @@
import base64 import base64
import math import math
import FPE import RNS
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
@ -11,8 +11,8 @@ from cryptography.hazmat.primitives.asymmetric import padding
class Destination: class Destination:
KEYSIZE = FPE.Identity.KEYSIZE; KEYSIZE = RNS.Identity.KEYSIZE;
PADDINGSIZE= FPE.Identity.PADDINGSIZE; PADDINGSIZE= RNS.Identity.PADDINGSIZE;
# Constants # Constants
SINGLE = 0x00 SINGLE = 0x00
@ -80,7 +80,7 @@ class Destination:
self.callback = None self.callback = None
self.proofcallback = None self.proofcallback = None
FPE.Transport.registerDestination(self) RNS.Transport.registerDestination(self)
def __str__(self): def __str__(self):
@ -192,5 +192,5 @@ class Destination:
if app_data != None: if app_data != None:
announce_data += app_data announce_data += app_data
FPE.Packet(self, announce_data, FPE.Packet.ANNOUNCE).send() RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE).send()

View File

@ -1,7 +1,7 @@
import base64 import base64
import math import math
import os import os
import FPE import RNS
import time import time
import atexit import atexit
import cPickle import cPickle
@ -38,40 +38,40 @@ class Identity:
@staticmethod @staticmethod
def remember(packet_hash, destination_hash, public_key, app_data = None): def remember(packet_hash, destination_hash, public_key, app_data = None):
FPE.log("Remembering "+FPE.prettyhexrep(destination_hash), FPE.LOG_VERBOSE) RNS.log("Remembering "+RNS.prettyhexrep(destination_hash), RNS.LOG_VERBOSE)
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data] Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
@staticmethod @staticmethod
def recall(destination_hash): def recall(destination_hash):
FPE.log("Searching for "+FPE.prettyhexrep(destination_hash)+"...", FPE.LOG_DEBUG) RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_DEBUG)
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])
FPE.log("Found "+FPE.prettyhexrep(destination_hash)+" in known destinations", FPE.LOG_DEBUG) RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_DEBUG)
return identity return identity
else: else:
FPE.log("Could not find "+FPE.prettyhexrep(destination_hash)+" in known destinations", FPE.LOG_DEBUG) RNS.log("Could not find "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_DEBUG)
return None return None
@staticmethod @staticmethod
def saveKnownDestinations(): def saveKnownDestinations():
FPE.log("Saving known destinations to storage...", FPE.LOG_VERBOSE) RNS.log("Saving known destinations to storage...", RNS.LOG_VERBOSE)
file = open(FPE.FlexPE.storagepath+"/known_destinations","w") file = open(RNS.Reticulum.storagepath+"/known_destinations","w")
cPickle.dump(Identity.known_destinations, file) cPickle.dump(Identity.known_destinations, file)
file.close() file.close()
FPE.log("Done saving known destinations to storage", FPE.LOG_VERBOSE) RNS.log("Done saving known destinations to storage", RNS.LOG_VERBOSE)
@staticmethod @staticmethod
def loadKnownDestinations(): def loadKnownDestinations():
if os.path.isfile(FPE.FlexPE.storagepath+"/known_destinations"): if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"):
file = open(FPE.FlexPE.storagepath+"/known_destinations","r") file = open(RNS.Reticulum.storagepath+"/known_destinations","r")
Identity.known_destinations = cPickle.load(file) Identity.known_destinations = cPickle.load(file)
file.close() file.close()
FPE.log("Loaded "+str(len(Identity.known_destinations))+" known destinations from storage", FPE.LOG_VERBOSE) RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destinations from storage", RNS.LOG_VERBOSE)
else: else:
FPE.log("Destinations file does not exist, so no known destinations loaded", FPE.LOG_VERBOSE) RNS.log("Destinations file does not exist, so no known destinations loaded", RNS.LOG_VERBOSE)
@staticmethod @staticmethod
def fullHash(data): def fullHash(data):
@ -89,8 +89,8 @@ class Identity:
@staticmethod @staticmethod
def validateAnnounce(packet): def validateAnnounce(packet):
if packet.packet_type == FPE.Packet.ANNOUNCE: if packet.packet_type == RNS.Packet.ANNOUNCE:
FPE.log("Validating announce from "+FPE.prettyhexrep(packet.destination_hash), FPE.LOG_VERBOSE) RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_VERBOSE)
destination_hash = packet.destination_hash destination_hash = packet.destination_hash
public_key = packet.data[10:Identity.DERKEYSIZE/8+10] public_key = packet.data[10:Identity.DERKEYSIZE/8+10]
random_hash = packet.data[Identity.DERKEYSIZE/8+10:Identity.DERKEYSIZE/8+20] random_hash = packet.data[Identity.DERKEYSIZE/8+10:Identity.DERKEYSIZE/8+20]
@ -105,13 +105,13 @@ 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):
FPE.log("Announce is valid", FPE.LOG_VERBOSE) RNS.log("Announce is valid", RNS.LOG_VERBOSE)
FPE.Identity.remember(FPE.Identity.fullHash(packet.raw), destination_hash, public_key) RNS.Identity.remember(RNS.Identity.fullHash(packet.raw), destination_hash, public_key)
FPE.log("Stored valid announce from "+FPE.prettyhexrep(destination_hash), FPE.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
else: else:
FPE.log("Announce is invalid", FPE.LOG_VERBOSE) RNS.log("Announce is invalid", RNS.LOG_VERBOSE)
del announced_identity del announced_identity
return False return False
@ -139,7 +139,7 @@ class Identity:
self.updateHashes() self.updateHashes()
FPE.log("Identity keys created for "+FPE.prettyhexrep(self.hash), FPE.LOG_INFO) RNS.log("Identity keys created for "+RNS.prettyhexrep(self.hash), RNS.LOG_INFO)
def getPrivateKey(self): def getPrivateKey(self):
return self.prv_bytes return self.prv_bytes
@ -163,7 +163,7 @@ class Identity:
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend()) self.pub = load_der_public_key(self.pub_bytes, backend=default_backend())
self.updateHashes() self.updateHashes()
except Exception as e: except Exception as e:
FPE.log("Error while loading public key, the contained exception was: "+str(e), FPE.LOG_ERROR) RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
def updateHashes(self): def updateHashes(self):
self.hash = Identity.truncatedHash(self.pub_bytes) self.hash = Identity.truncatedHash(self.pub_bytes)
@ -223,7 +223,7 @@ class Identity:
) )
) )
except: except:
FPE.log("Decryption by "+FPE.prettyhexrep(self.hash)+" failed") RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed")
return plaintext; return plaintext;
else: else:
@ -264,7 +264,7 @@ class Identity:
def prove(self, packet, destination): def prove(self, packet, destination):
proof_data = packet.packet_hash + self.sign(packet.packet_hash) proof_data = packet.packet_hash + self.sign(packet.packet_hash)
proof = FPE.Packet(destination, proof_data, FPE.Packet.PROOF) proof = RNS.Packet(destination, proof_data, RNS.Packet.PROOF)
proof.send() proof.send()

View File

@ -5,7 +5,7 @@ import sys
import serial import serial
import threading import threading
import time import time
import FPE import RNS
class SerialInterface(Interface): class SerialInterface(Interface):
MAX_CHUNK = 32768 MAX_CHUNK = 32768
@ -36,7 +36,7 @@ class SerialInterface(Interface):
self.parity = serial.PARITY_ODD self.parity = serial.PARITY_ODD
try: try:
FPE.log("Opening serial port "+self.port+"...") RNS.log("Opening serial port "+self.port+"...")
self.serial = serial.Serial( self.serial = serial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -51,7 +51,7 @@ class SerialInterface(Interface):
dsrdtr = False, dsrdtr = False,
) )
except Exception as e: except Exception as e:
FPE.log("Could not create serial port", FPE.LOG_ERROR) RNS.log("Could not create serial port", RNS.LOG_ERROR)
raise e raise e
if self.serial.is_open: if self.serial.is_open:
@ -60,13 +60,13 @@ class SerialInterface(Interface):
thread.setDaemon(True) thread.setDaemon(True)
thread.start() thread.start()
self.online = True self.online = True
FPE.log("Serial port "+self.port+" is now open") RNS.log("Serial port "+self.port+" is now open")
else: else:
raise IOError("Could not open serial port") raise IOError("Could not open serial port")
def processIncoming(self, data): def processIncoming(self, data):
self.owner.inbound(data) self.owner.inbound(data, self)
def processOutgoing(self,data): def processOutgoing(self,data):
@ -93,6 +93,8 @@ class SerialInterface(Interface):
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
FPE.log("A serial port error occurred, the contained exception was: "+str(e), FPE.LOG_ERROR) RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
FPE.log("The interface "+str(self.name)+" is now offline. Restart FlexPE to attempt reconnection.", FPE.LOG_ERROR) RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
def __str__(self):
return "SerialInterface["+self.name+"]"

View File

@ -0,0 +1,214 @@
from __future__ import print_function
from Interface import Interface
from time import sleep
import sys
import serial
import threading
import time
import RNS
class KISS():
FEND = chr(0xC0)
FESC = chr(0xDB)
TFEND = chr(0xDC)
TFESC = chr(0xDD)
CMD_UNKNOWN = chr(0xFE)
CMD_DATA = chr(0x00)
CMD_TXDELAY = chr(0x01)
CMD_P = chr(0x02)
CMD_SLOTTIME = chr(0x03)
CMD_TXTAIL = chr(0x04)
CMD_FULLDUPLEX = chr(0x05)
CMD_SETHARDWARE = chr(0x06)
CMD_RETURN = chr(0xFF)
class SerialKISSInterface(Interface):
MAX_CHUNK = 32768
owner = None
port = None
speed = None
databits = None
parity = None
stopbits = None
serial = None
def __init__(self, owner, port, speed, databits, parity, stopbits, preamble, txtail, persistence, slottime):
self.serial = None
self.owner = owner
self.port = port
self.speed = speed
self.databits = databits
self.parity = serial.PARITY_NONE
self.stopbits = stopbits
self.timeout = 100
self.online = False
self.preamble = preamble if preamble != None else 350;
self.txtail = txtail if txtail != None else 20;
self.persistence = persistence if persistence != None else 64;
self.slottime = slottime if slottime != None else 20;
if parity.lower() == "e" or parity.lower() == "even":
self.parity = serial.PARITY_EVEN
if parity.lower() == "o" or parity.lower() == "odd":
self.parity = serial.PARITY_ODD
try:
RNS.log("Opening serial port "+self.port+"...")
self.serial = serial.Serial(
port = self.port,
baudrate = self.speed,
bytesize = self.databits,
parity = self.parity,
stopbits = self.stopbits,
xonxoff = False,
rtscts = False,
timeout = 0,
inter_byte_timeout = None,
write_timeout = None,
dsrdtr = False,
)
except Exception as e:
RNS.log("Could not create serial port", RNS.LOG_ERROR)
raise e
if self.serial.is_open:
# Allow time for interface to initialise before config
sleep(2.0)
thread = threading.Thread(target=self.readLoop)
thread.setDaemon(True)
thread.start()
self.online = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble)
self.setTxTail(self.txtail)
self.setPersistence(self.persistence)
self.setSlotTime(self.slottime)
RNS.log("KISS interface configured")
sleep(2)
else:
raise IOError("Could not open serial port")
def setPreamble(self, preamble):
preamble_ms = preamble
preamble = int(preamble_ms / 10)
if preamble < 0:
preamble = 0
if preamble > 255:
preamble = 255
RNS.log("Setting preamble to "+str(preamble)+" "+chr(preamble))
kiss_command = KISS.FEND+KISS.CMD_TXDELAY+chr(preamble)+KISS.FEND
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")")
def setTxTail(self, txtail):
txtail_ms = txtail
txtail = int(txtail_ms / 10)
if txtail < 0:
txtail = 0
if txtail > 255:
txtail = 255
kiss_command = KISS.FEND+KISS.CMD_TXTAIL+chr(txtail)+KISS.FEND
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")")
def setPersistence(self, persistence):
if persistence < 0:
persistence = 0
if persistence > 255:
persistence = 255
kiss_command = KISS.FEND+KISS.CMD_P+chr(persistence)+KISS.FEND
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface persistence to "+str(persistence))
def setSlotTime(self, slottime):
slottime_ms = slottime
slottime = int(slottime_ms / 10)
if slottime < 0:
slottime = 0
if slottime > 255:
slottime = 255
kiss_command = KISS.FEND+KISS.CMD_SLOTTIME+chr(slottime)+KISS.FEND
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")")
def processIncoming(self, data):
self.owner.inbound(data, self)
def processOutgoing(self,data):
if self.online:
data = data.replace(chr(0xdb), chr(0xdb)+chr(0xdd))
data = data.replace(chr(0xc0), chr(0xdb)+chr(0xdc))
frame = chr(0xc0)+chr(0x00)+data+chr(0xc0)
written = self.serial.write(frame)
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
def readLoop(self):
try:
in_frame = False
escape = False
command = KISS.CMD_UNKNOWN
data_buffer = ""
last_read_ms = int(time.time()*1000)
while self.serial.is_open:
if self.serial.in_waiting:
byte = self.serial.read(1)
last_read_ms = int(time.time()*1000)
if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA):
in_frame = False
self.processIncoming(data_buffer)
elif (byte == KISS.FEND):
in_frame = True
command = KISS.CMD_UNKNOWN
data_buffer = ""
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
# We only support one HDLC port for now, so
# strip off port nibble
byte = chr(ord(byte) & 0x0F)
command = byte
elif (command == KISS.CMD_DATA):
if (byte == KISS.FESC):
escape = True
else:
if (escape):
if (byte == KISS.TFEND):
byte = KISS.FEND
if (byte == KISS.TFESC):
byte = KISS.FESC
data_buffer = data_buffer+byte
else:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
data_buffer = ""
in_frame = False
command = KISS.CMD_UNKNOWN
escape = False
sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
def __str__(self):
return "SerialKISSInterface["+self.name+"]"

View File

@ -3,7 +3,7 @@ import SocketServer
import threading import threading
import socket import socket
import sys import sys
import FPE import RNS
class UdpInterface(Interface): class UdpInterface(Interface):
bind_ip = None bind_ip = None
@ -37,7 +37,7 @@ class UdpInterface(Interface):
def processIncoming(self, data): def processIncoming(self, data):
self.owner.inbound(data) self.owner.inbound(data, self)
def processOutgoing(self,data): def processOutgoing(self,data):
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@ -46,7 +46,7 @@ class UdpInterface(Interface):
def __str__(self): def __str__(self):
return "UdpInterface["+self.bind_ip+":"+str(self.bind_port)+"]" return "UdpInterface["+self.name+"/"+self.bind_ip+":"+str(self.bind_port)+"]"
class UdpInterfaceHandler(SocketServer.BaseRequestHandler): class UdpInterfaceHandler(SocketServer.BaseRequestHandler):
interface = None interface = None

View File

@ -1,6 +1,6 @@
import struct import struct
import time import time
import FPE import RNS
class Packet: class Packet:
# Constants # Constants
@ -16,10 +16,10 @@ class Packet:
HEADER_4 = 0x03; # Reserved HEADER_4 = 0x03; # Reserved
header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4] header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4]
def __init__(self, destination, data, packet_type = RESOURCE, transport_type = FPE.Transport.BROADCAST, header_type = HEADER_1, transport_id = None): def __init__(self, destination, data, packet_type = RESOURCE, transport_type = RNS.Transport.BROADCAST, header_type = HEADER_1, transport_id = None):
if destination != None: if destination != None:
if transport_type == None: if transport_type == None:
transport_type = FPE.Transport.BROADCAST transport_type = RNS.Transport.BROADCAST
self.header_type = header_type self.header_type = header_type
self.packet_type = packet_type self.packet_type = packet_type
@ -94,9 +94,9 @@ class Packet:
def send(self): def send(self):
if not self.sent: if not self.sent:
self.pack() self.pack()
FPE.log("Size: "+str(len(self.raw))+" header is "+str(len(self.header))+" payload is "+str(len(self.ciphertext)), FPE.LOG_DEBUG) RNS.log("Size: "+str(len(self.raw))+" header is "+str(len(self.header))+" payload is "+str(len(self.ciphertext)), RNS.LOG_DEBUG)
FPE.Transport.outbound(self.raw) RNS.Transport.outbound(self.raw)
self.packet_hash = FPE.Identity.fullHash(self.raw) self.packet_hash = RNS.Identity.fullHash(self.raw)
self.sent_at = time.time() self.sent_at = time.time()
self.sent = True self.sent = True
else: else:

165
RNS/Reticulum.py Executable file
View File

@ -0,0 +1,165 @@
from Interfaces import *
import ConfigParser
from vendor.configobj import ConfigObj
import atexit
import struct
import array
import os.path
import os
import RNS
import traceback
class Reticulum:
MTU = 500
router = None
config = None
configdir = os.path.expanduser("~")+"/.reticulum"
configpath = ""
storagepath = ""
cachepath = ""
def __init__(self,configdir=None):
if configdir != None:
Reticulum.configdir = configdir
Reticulum.configpath = Reticulum.configdir+"/config"
Reticulum.storagepath = Reticulum.configdir+"/storage"
Reticulum.cachepath = Reticulum.configdir+"/storage/cache"
if not os.path.isdir(Reticulum.storagepath):
os.makedirs(Reticulum.storagepath)
if not os.path.isdir(Reticulum.cachepath):
os.makedirs(Reticulum.cachepath)
if os.path.isfile(self.configpath):
self.config = ConfigObj(self.configpath)
RNS.log("Configuration loaded from "+self.configpath)
else:
RNS.log("Could not load config file, creating default configuration...")
self.createDefaultConfig()
RNS.log("Default config file created. Make any necessary changes in "+Reticulum.configdir+"/config and start Reticulum again.")
RNS.log("Exiting now!")
exit(1)
self.applyConfig()
RNS.Identity.loadKnownDestinations()
Reticulum.router = self
atexit.register(RNS.Identity.exitHandler)
def applyConfig(self):
if "logging" in self.config:
for option in self.config["logging"]:
value = self.config["logging"][option]
if option == "loglevel":
RNS.loglevel = int(value)
for name in self.config["interfaces"]:
c = self.config["interfaces"][name]
try:
if c["type"] == "UdpInterface":
interface = UdpInterface.UdpInterface(
RNS.Transport,
c["listen_ip"],
int(c["listen_port"]),
c["forward_ip"],
int(c["forward_port"])
)
if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True
else:
interface.OUT = False
interface.name = name
RNS.Transport.interfaces.append(interface)
if c["type"] == "SerialInterface":
port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8
parity = c["parity"] if "parity" in c else "N"
stopbits = int(c["stopbits"]) if "stopbits" in c else 1
if port == None:
raise ValueError("No port specified for serial interface")
interface = SerialKISSInterface.SerialKISSInterface(
RNS.Transport,
port,
speed,
databits,
parity,
stopbits
)
if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True
else:
interface.OUT = False
interface.name = name
RNS.Transport.interfaces.append(interface)
if c["type"] == "SerialKISSInterface":
preamble = int(c["preamble"]) if "preamble" in c else None
txtail = int(c["txtail"]) if "txtail" in c else None
persistence = int(c["persistence"]) if "persistence" in c else None
slottime = int(c["slottime"]) if "slottime" in c else None
port = c["port"] if "port" in c else None
speed = int(c["speed"]) if "speed" in c else 9600
databits = int(c["databits"]) if "databits" in c else 8
parity = c["parity"] if "parity" in c else "N"
stopbits = int(c["stopbits"]) if "stopbits" in c else 1
if port == None:
raise ValueError("No port specified for serial interface")
interface = SerialKISSInterface.SerialKISSInterface(
RNS.Transport,
port,
speed,
databits,
parity,
stopbits,
preamble,
txtail,
persistence,
slottime
)
if "outgoing" in c and c["outgoing"].lower() == "true":
interface.OUT = True
else:
interface.OUT = False
interface.name = name
RNS.Transport.interfaces.append(interface)
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 contained exception was: "+str(e), RNS.LOG_ERROR)
traceback.print_exc()
def createDefaultConfig(self):
self.config = ConfigObj()
self.config.filename = Reticulum.configpath
self.config["interfaces"] = {}
self.config["interfaces"]["Default UDP Interface"] = {}
self.config["interfaces"]["Default UDP Interface"]["type"] = "UdpInterface"
self.config["interfaces"]["Default UDP Interface"]["listen_ip"] = "0.0.0.0"
self.config["interfaces"]["Default UDP Interface"]["listen_port"] = 7777
self.config["interfaces"]["Default UDP Interface"]["forward_ip"] = "255.255.255.255"
self.config["interfaces"]["Default UDP Interface"]["forward_port"] = 7777
self.config["interfaces"]["Default UDP Interface"]["use_as_outgoing"] = "true"
if not os.path.isdir(Reticulum.configdir):
os.makedirs(Reticulum.configdir)
self.config.write()
self.applyConfig()

View File

@ -1,4 +1,4 @@
import FPE import RNS
class Transport: class Transport:
# Constants # Constants
@ -17,31 +17,32 @@ class Transport:
Transport.cacheRaw(raw) Transport.cacheRaw(raw)
for interface in Transport.interfaces: for interface in Transport.interfaces:
if interface.OUT: if interface.OUT:
FPE.log("Transmitting via: "+str(interface), FPE.LOG_DEBUG) RNS.log("Transmitting via: "+str(interface), RNS.LOG_DEBUG)
interface.processOutgoing(raw) interface.processOutgoing(raw)
@staticmethod @staticmethod
def inbound(raw): def inbound(raw, interface=None):
packet_hash = FPE.Identity.fullHash(raw) packet_hash = RNS.Identity.fullHash(raw)
RNS.log(str(interface)+" received packet with hash "+RNS.prettyhexrep(packet_hash), RNS.LOG_DEBUG)
if not packet_hash in Transport.packet_hashlist: if not packet_hash in Transport.packet_hashlist:
Transport.packet_hashlist.append(packet_hash) Transport.packet_hashlist.append(packet_hash)
packet = FPE.Packet(None, raw) packet = RNS.Packet(None, raw)
packet.unpack() packet.unpack()
packet.packet_hash = packet_hash packet.packet_hash = packet_hash
if packet.packet_type == FPE.Packet.ANNOUNCE: if packet.packet_type == RNS.Packet.ANNOUNCE:
if FPE.Identity.validateAnnounce(packet): if RNS.Identity.validateAnnounce(packet):
Transport.cache(packet) Transport.cache(packet)
if packet.packet_type == FPE.Packet.RESOURCE: if packet.packet_type == RNS.Packet.RESOURCE:
for destination in Transport.destinations: for destination in Transport.destinations:
if destination.hash == packet.destination_hash and destination.type == packet.destination_type: if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
packet.destination = destination packet.destination = destination
destination.receive(packet) destination.receive(packet)
Transport.cache(packet) Transport.cache(packet)
if packet.packet_type == FPE.Packet.PROOF: if packet.packet_type == RNS.Packet.PROOF:
for destination in Transport.destinations: for destination in Transport.destinations:
if destination.hash == packet.destination_hash: if destination.hash == packet.destination_hash:
if destination.proofcallback != None: if destination.proofcallback != None:
@ -50,21 +51,21 @@ class Transport:
@staticmethod @staticmethod
def registerDestination(destination): def registerDestination(destination):
destination.MTU = FPE.FlexPE.MTU destination.MTU = RNS.Reticulum.MTU
if destination.direction == FPE.Destination.IN: if destination.direction == RNS.Destination.IN:
Transport.destinations.append(destination) Transport.destinations.append(destination)
@staticmethod @staticmethod
def cache(packet): def cache(packet):
FPE.Transport.cacheRaw(packet.raw) RNS.Transport.cacheRaw(packet.raw)
@staticmethod @staticmethod
def cacheRaw(raw): def cacheRaw(raw):
try: try:
file = open(FPE.FlexPE.cachepath+"/"+FPE.hexrep(FPE.Identity.fullHash(raw), delimit=False), "w") file = open(RNS.Reticulum.cachepath+"/"+RNS.hexrep(RNS.Identity.fullHash(raw), delimit=False), "w")
file.write(raw) file.write(raw)
file.close() file.close()
FPE.log("Wrote packet "+FPE.prettyhexrep(FPE.Identity.fullHash(raw))+" to cache", FPE.LOG_DEBUG) RNS.log("Wrote packet "+RNS.prettyhexrep(RNS.Identity.fullHash(raw))+" to cache", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
FPE.log("Error writing packet to cache", FPE.LOG_ERROR) RNS.log("Error writing packet to cache", RNS.LOG_ERROR)
FPE.log("The contained exception was: "+str(e)) RNS.log("The contained exception was: "+str(e))

View File

@ -1,6 +1,6 @@
import argparse import argparse
import time import time
import FPE import RNS
# Let's define an app name. We'll use this for all # Let's define an app name. We'll use this for all
# destinations we create. Since this echo example # destinations we create. Since this echo example
@ -11,11 +11,11 @@ APP_NAME = "example_utilitites"
# This initialisation is executed when the users chooses # This initialisation is executed when the users chooses
# to run as a server # to run as a server
def server(configpath): def server(configpath):
# We must first initialise FlexPE # We must first initialise Reticulum
fpe = FPE.FlexPE(configpath) RNS = RNS.Reticulum(configpath)
# Randomly create a new identity for our echo server # Randomly create a new identity for our echo server
server_identity = FPE.Identity() server_identity = RNS.Identity()
# We create a destination that clients can query. We want # We create a destination that clients can query. We want
# to be able to verify echo replies to our clients, so we # to be able to verify echo replies to our clients, so we
@ -23,7 +23,7 @@ def server(configpath):
# messages. This way the client can send a request and be # messages. This way the client can send a request and be
# certain that no-one else than this destination was able # certain that no-one else than this destination was able
# to read it. # to read it.
echo_destination = FPE.Destination(server_identity, FPE.Destination.IN, FPE.Destination.SINGLE, APP_NAME, "echo", "request") echo_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
# Tell the destination which function in our program to # Tell the destination which function in our program to
# run when a packet is received. # run when a packet is received.
@ -36,7 +36,7 @@ def server(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
FPE.log("Echo server "+FPE.prettyhexrep(destination.hash)+" running, hit enter to send announce (Ctrl-C to quit)") RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to send announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.
# If the user just hits enter, we will announce our server # If the user just hits enter, we will announce our server
@ -45,7 +45,7 @@ def announceLoop(destination):
while True: while True:
entered = raw_input() entered = raw_input()
destination.announce() destination.announce()
FPE.log("Sent announce from "+FPE.prettyhexrep(destination.hash)) RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
def serverCallback(message, packet): def serverCallback(message, packet):
@ -61,7 +61,7 @@ def serverCallback(message, packet):
# We can now create a destination that will let us reach # We can now create a destination that will let us reach
# the client which send the echo request. # the client which send the echo request.
reply_destination = FPE.Destination(None, FPE.Destination.OUT, FPE.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity_hexhash) reply_destination = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity_hexhash)
# Let's encode the reply destination hash in a readable # Let's encode the reply destination hash in a readable
# way, so we can output some info to the user. # way, so we can output some info to the user.
@ -69,10 +69,10 @@ def serverCallback(message, packet):
# Tell the user that we received an echo request, and # Tell the user that we received an echo request, and
# that we are going to send a reply to the requester. # that we are going to send a reply to the requester.
FPE.log("Received packet from <"+reply_destination_hexhash+">, sending reply") RNS.log("Received packet from <"+reply_destination_hexhash+">, sending reply")
# To let the client know that we got the echo request, # To let the client know that we got the echo request,
# we will use the "proof" functions of FlexPE. In most # we will use the "proof" functions of Reticulum. In most
# applications, the proving of packets will occur fully # applications, the proving of packets will occur fully
# automatically, but in some cases like this, it can be # automatically, but in some cases like this, it can be
# beneficial to use the functions manually, since it # beneficial to use the functions manually, since it
@ -98,14 +98,14 @@ def client(destination_hexhash, configpath):
raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)") raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
destination_hash = destination_hexhash.decode("hex") destination_hash = destination_hexhash.decode("hex")
except: except:
FPE.log("Invalid destination entered. Check your input!") RNS.log("Invalid destination entered. Check your input!")
exit() exit()
# We must first initialise FlexPE # We must first initialise Reticulum
fpe = FPE.FlexPE(configpath) RNS = RNS.Reticulum(configpath)
# Randomly create a new identity for our echo server # Randomly create a new identity for our echo server
client_identity = FPE.Identity() client_identity = RNS.Identity()
# Let's set up a destination for replies to our echo # Let's set up a destination for replies to our echo
# requests. This destination will be used by the server # requests. This destination will be used by the server
@ -124,7 +124,7 @@ def client(destination_hexhash, configpath):
# create a unique destination for the server to respond to. # create a unique destination for the server to respond to.
# If we had used a "single" destination, something equivalent # If we had used a "single" destination, something equivalent
# to this process would have happened automatically. # to this process would have happened automatically.
reply_destination = FPE.Destination(client_identity, FPE.Destination.IN, FPE.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity.hexhash) reply_destination = RNS.Destination(client_identity, RNS.Destination.IN, RNS.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity.hexhash)
# Since we are only expecting packets of the "proof" # Since we are only expecting packets of the "proof"
# type to reach our reply destination, we just set the # type to reach our reply destination, we just set the
@ -133,7 +133,7 @@ def client(destination_hexhash, configpath):
reply_destination.setProofCallback(clientProofCallback) reply_destination.setProofCallback(clientProofCallback)
# Tell the user that the client is ready! # Tell the user that the client is ready!
FPE.log("Echo client "+FPE.prettyhexrep(reply_destination.hash)+" ready, hit enter to send echo request (Ctrl-C to quit)") RNS.log("Echo client "+RNS.prettyhexrep(reply_destination.hash)+" ready, hit enter to send echo request (Ctrl-C to quit)")
# We enter a loop that runs until the user exits. # We enter a loop that runs until the user exits.
# If the user hits enter, we will try to send an # If the user hits enter, we will try to send an
@ -142,12 +142,12 @@ def client(destination_hexhash, configpath):
while True: while True:
raw_input() raw_input()
# 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 FlexPE 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
# Identity module. If the destination is known, it will # Identity module. If the destination is known, it will
# return an Identity instance that can be used in # return an Identity instance that can be used in
# outgoing destinations. # outgoing destinations.
server_identity = FPE.Identity.recall(destination_hash) server_identity = RNS.Identity.recall(destination_hash)
if server_identity != None: 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
@ -156,7 +156,7 @@ def client(destination_hexhash, configpath):
# Since this is a "single" destination, the identity # Since this is a "single" destination, the identity
# hash will be automatically added to the end of # hash will be automatically added to the end of
# the name. # the name.
request_destination = FPE.Destination(server_identity, FPE.Destination.OUT, FPE.Destination.SINGLE, APP_NAME, "echo", "request") request_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
# The destination is ready, so let's create a packet. # The destination is ready, so let's create a packet.
# We set the destination to the request_destination # We set the destination to the request_destination
@ -164,7 +164,7 @@ def client(destination_hexhash, configpath):
# is the identity hash of our client identity. # is the identity hash of our client identity.
# Including that information will let the server # Including that information will let the server
# create a destination to send replies to. # create a destination to send replies to.
echo_request = FPE.Packet(request_destination, client_identity.hash) echo_request = RNS.Packet(request_destination, client_identity.hash)
# Send the packet! # Send the packet!
echo_request.send() echo_request.send()
@ -173,11 +173,11 @@ def client(destination_hexhash, configpath):
sent_requests.append(echo_request) sent_requests.append(echo_request)
# Tell the user that the echo request was sent # Tell the user that the echo request was sent
FPE.log("Sent echo request to "+FPE.prettyhexrep(request_destination.hash)) RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
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.
FPE.log("Destination is not yet known. Wait for an announce to arrive.") RNS.log("Destination is not yet known. Wait for an announce to arrive.")
# This method is called when our reply destination # This method is called when our reply destination
# receives a proof packet. # receives a proof packet.
@ -209,8 +209,8 @@ def clientProofCallback(proof_packet):
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = str(rtt)+" milliseconds"
FPE.log( RNS.log(
"Valid echo reply, proved by "+FPE.prettyhexrep(unproven_packet.destination.hash)+ "Valid echo reply, proved by "+RNS.prettyhexrep(unproven_packet.destination.hash)+
", round-trip time was "+rttstring ", round-trip time was "+rttstring
) )
# Perform some cleanup # Perform some cleanup
@ -219,9 +219,9 @@ def clientProofCallback(proof_packet):
else: else:
# If the proof was invalid, we inform # If the proof was invalid, we inform
# the user of this. # the user of this.
FPE.log("Echo reply received, but proof was invalid") RNS.log("Echo reply received, but proof was invalid")
except: except:
FPE.log("Proof packet received, but packet contained invalid or unparsable data") RNS.log("Proof packet received, but packet contained invalid or unparsable data")
@ -231,7 +231,7 @@ if __name__ == "__main__":
try: try:
parser = argparse.ArgumentParser(description="Simple echo server and client utility") parser = argparse.ArgumentParser(description="Simple echo server and client utility")
parser.add_argument("-s", "--server", action="store_true", help="wait for incoming packets from clients") parser.add_argument("-s", "--server", action="store_true", help="wait for incoming packets from clients")
parser.add_argument("--config", action="store", default=None, help="path to alternative FlexPE config directory", type=str) parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str) parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str)
args = parser.parse_args() args = parser.parse_args()

View File

@ -2,7 +2,7 @@ import os
import glob import glob
import time import time
from .FlexPE import FlexPE from .Reticulum import Reticulum
from .Identity import Identity from .Identity import Identity
from .Transport import Transport from .Transport import Transport
from .Destination import Destination from .Destination import Destination

14
t.py
View File

@ -1,8 +1,8 @@
# from FPE.Destination import * # from RNS.Destination import *
# from FPE.Packet import * # from RNS.Packet import *
# from FPE import FlexPE # from RNS import Reticulum
from FPE import * from RNS import *
# from FPE import Destination # from RNS import Destination
import os import os
import time import time
@ -12,8 +12,8 @@ def testCallback(message, receiver):
print("----------") print("----------")
#fpe = FlexPE(configdir=os.path.expanduser("~")+"/.flexpe2") #RNS = Reticulum(configdir=os.path.expanduser("~")+"/.Reticulum2")
fpe = FlexPE() RNS = Reticulum()
identity = Identity() identity = Identity()
d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user") d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user")