Compare commits

...

11 Commits

Author SHA1 Message Date
markqvist 6b20a98adc Update README.md 2022-01-12 12:53:18 +01:00
Mark Qvist f3d04ba90f Improved AutoInterface handling on Android 2022-01-12 12:12:04 +01:00
Mark Qvist 1d2564cedb Interface import on Android 2022-01-12 12:02:00 +01:00
Mark Qvist bec8473695 Better Android detection 2022-01-12 11:50:03 +01:00
Mark Qvist 25620415a0 Updated platform utils 2022-01-12 11:18:24 +01:00
Mark Qvist b6df952995 Platform version check for Windows 2022-01-12 10:16:59 +01:00
Mark Qvist a72aaf12ca Platform version check for Windows 2022-01-12 10:07:44 +01:00
Mark Qvist b978a993b2 Version update 2022-01-11 03:07:34 +01:00
Mark Qvist 5ae00264e8 Preliminaly ESP32 support for RNodeInterface 2022-01-11 03:07:03 +01:00
Mark Qvist 5396b80e80 Updated example 2022-01-11 03:06:35 +01:00
Mark Qvist fdaa58a6fa Improved malformed packet detection 2022-01-11 03:06:16 +01:00
10 changed files with 164 additions and 50 deletions
+6
View File
@@ -1,6 +1,12 @@
##########################################################
# This RNS example demonstrates a simple speedtest #
# program to measure link throughput. #
# #
# The current configuration is suited for testing fast #
# links. If you want to measure slow links like LoRa or #
# packet radio, you must significantly lower the #
# data_cap variable, which defines how much data is sent #
# for each test. #
##########################################################
import os
+1
View File
@@ -9,6 +9,7 @@ Having no dependencies on traditional networking stacks free up overhead that ha
No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.
## Read The Manual
The full documentation for Reticulum is available at [markqvist.github.io/Reticulum/manual/](https://markqvist.github.io/Reticulum/manual/).
You can also [download the Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf)
+5 -2
View File
@@ -22,6 +22,7 @@ class AutoInterface(Interface):
PEERING_TIMEOUT = 6.0
DARWIN_IGNORE_IFS = ["awdl0", "llw0", "lo0", "en5"]
ANDROID_IGNORE_IFS = ["dummy0", "lo", "tun0"]
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None):
import importlib
@@ -101,10 +102,12 @@ class AutoInterface(Interface):
suitable_interfaces = 0
for ifname in self.netifaces.interfaces():
if RNS.vendor.platformutils.get_platform() == "darwin" and ifname in AutoInterface.DARWIN_IGNORE_IFS:
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.get_platform() == "darwin" and ifname == "lo0":
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
RNS.log(str(self)+" skipping Darwin loopback interface "+str(ifname), RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.is_android() and ifname in AutoInterface.ANDROID_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Android system interface "+str(ifname), RNS.LOG_EXTREME)
elif ifname in self.ignored_interfaces:
RNS.log(str(self)+" ignoring disallowed interface "+str(ifname), RNS.LOG_EXTREME)
else:
+54 -1
View File
@@ -30,8 +30,11 @@ class KISS():
CMD_STAT_SNR = 0x24
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_PLATFORM = 0x48
CMD_MCU = 0x49
CMD_FW_VERSION = 0x50
CMD_ROM_READ = 0x51
CMD_RESET = 0x55
DETECT_REQ = 0x73
DETECT_RESP = 0x46
@@ -45,6 +48,9 @@ class KISS():
ERROR_TXFAILED = 0x02
ERROR_EEPROM_LOCKED = 0x03
PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80
@staticmethod
def escape(data):
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
@@ -101,6 +107,9 @@ class RNodeInterface(Interface):
self.cr = cr
self.state = KISS.RADIO_STATE_OFF
self.bitrate = 0
self.platform = None
self.mcu = None
self.detected = False
self.last_id = 0
self.first_tx = None
@@ -190,6 +199,17 @@ class RNodeInterface(Interface):
thread = threading.Thread(target=self.readLoop)
thread.setDaemon(True)
thread.start()
self.detect()
sleep(0.1)
if not self.detected:
raise IOError("Could not detect device")
else:
if self.platform == KISS.PLATFORM_ESP32:
RNS.log("Resetting ESP32-based device before configuration...", RNS.LOG_VERBOSE)
self.hard_reset()
self.online = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
@@ -214,6 +234,19 @@ class RNodeInterface(Interface):
self.setCodingRate()
self.setRadioState(KISS.RADIO_STATE_ON)
def detect(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+self(str))
def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device")
sleep(2);
def setFrequency(self):
c1 = self.frequency >> 24
c2 = self.frequency >> 16 & 0xFF
@@ -420,6 +453,11 @@ class RNodeInterface(Interface):
self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte
# if self.r_state:
# RNS.log(str(self)+" Radio reporting state is online ("+RNS.hexrep([self.r_state])+")", RNS.LOG_DEBUG)
# else:
# RNS.log(str(self)+" Radio reporting state is offline ("+RNS.hexrep([self.r_state])+")", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte
elif (command == KISS.CMD_STAT_RX):
@@ -456,6 +494,10 @@ class RNodeInterface(Interface):
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
elif (command == KISS.CMD_RANDOM):
self.r_random = byte
elif (command == KISS.CMD_PLATFORM):
self.platform = byte
elif (command == KISS.CMD_MCU):
self.mcu = byte
elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
@@ -463,8 +505,19 @@ class RNodeInterface(Interface):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
elif (command == KISS.CMD_RESET):
if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32:
if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset")
elif (command == KISS.CMD_READY):
self.process_queue()
elif (command == KISS.CMD_DETECT):
if byte == KISS.DETECT_RESP:
self.detected = True
else:
self.detected = False
else:
time_since_last = int(time.time()*1000) - last_read_ms
@@ -500,7 +553,7 @@ class RNodeInterface(Interface):
def reconnect_port(self):
while not self.online:
try:
time.sleep(5)
time.sleep(3.5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
+1 -1
View File
@@ -765,7 +765,7 @@ class Link:
return plaintext
except Exception as e:
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(traceback.format_exc(), RNS.LOG_ERROR)
# RNS.log(traceback.format_exc(), RNS.LOG_ERROR)
# TODO: Think long about implications here
# self.teardown()
+24 -18
View File
@@ -185,27 +185,33 @@ class Packet:
def unpack(self):
self.flags = self.raw[0]
self.hops = self.raw[1]
try:
self.flags = self.raw[0]
self.hops = self.raw[1]
self.header_type = (self.flags & 0b11000000) >> 6
self.transport_type = (self.flags & 0b00110000) >> 4
self.destination_type = (self.flags & 0b00001100) >> 2
self.packet_type = (self.flags & 0b00000011)
self.header_type = (self.flags & 0b11000000) >> 6
self.transport_type = (self.flags & 0b00110000) >> 4
self.destination_type = (self.flags & 0b00001100) >> 2
self.packet_type = (self.flags & 0b00000011)
if self.header_type == Packet.HEADER_2:
self.transport_id = self.raw[2:12]
self.destination_hash = self.raw[12:22]
self.context = ord(self.raw[22:23])
self.data = self.raw[23:]
else:
self.transport_id = None
self.destination_hash = self.raw[2:12]
self.context = ord(self.raw[12:13])
self.data = self.raw[13:]
if self.header_type == Packet.HEADER_2:
self.transport_id = self.raw[2:12]
self.destination_hash = self.raw[12:22]
self.context = ord(self.raw[22:23])
self.data = self.raw[23:]
else:
self.transport_id = None
self.destination_hash = self.raw[2:12]
self.context = ord(self.raw[12:13])
self.data = self.raw[13:]
self.packed = False
self.update_hash()
self.packed = False
self.update_hash()
return True
except Exception as e:
RNS.log("Received malformed packet, dropping it. The contained exception was: "+str(e), RNS.LOG_EXTREME)
return False
def send(self):
"""
+35 -23
View File
@@ -1,8 +1,11 @@
from .vendor.platformutils import get_platform
if get_platform() == "android":
# TODO: Selectively import Android-relevant interfaces
pass
from .Interfaces import Interface
from .Interfaces import LocalInterface
from .Interfaces import AutoInterface
from .Interfaces import TCPInterface
from .Interfaces import UDPInterface
else:
from .Interfaces import *
@@ -116,6 +119,8 @@ class Reticulum:
:param configdir: Full path to a Reticulum configuration directory.
"""
RNS.vendor.platformutils.platform_checks()
if configdir != None:
Reticulum.configdir = configdir
@@ -276,30 +281,35 @@ class Reticulum:
try:
if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True:
if c["type"] == "AutoInterface":
group_id = c["group_id"] if "group_id" in c else None
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
data_port = int(c["data_port"]) if "data_port" in c else None
allowed_interfaces = c.as_list("devices") if "devices" in c else None
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
if not RNS.vendor.platformutils.is_windows():
group_id = c["group_id"] if "group_id" in c else None
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
data_port = int(c["data_port"]) if "data_port" in c else None
allowed_interfaces = c.as_list("devices") if "devices" in c else None
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
interface = AutoInterface.AutoInterface(
RNS.Transport,
name,
group_id,
discovery_scope,
discovery_port,
data_port,
allowed_interfaces,
ignored_interfaces
)
interface = AutoInterface.AutoInterface(
RNS.Transport,
name,
group_id,
discovery_scope,
discovery_port,
data_port,
allowed_interfaces,
ignored_interfaces
)
if "outgoing" in c and c.as_bool("outgoing") == True:
interface.OUT = True
if "outgoing" in c and c.as_bool("outgoing") == True:
interface.OUT = True
else:
interface.OUT = False
RNS.Transport.interfaces.append(interface)
else:
interface.OUT = False
RNS.Transport.interfaces.append(interface)
RNS.log("AutoInterface is not currently supported on Windows, disabling interface.", RNS.LOG_ERROR);
RNS.log("Please remove this AutoInterface instance from your configuration file.", RNS.LOG_ERROR);
RNS.log("You will have to manually configure other interfaces for connectivity.", RNS.LOG_ERROR);
if c["type"] == "UDPInterface":
@@ -531,6 +541,8 @@ class Reticulum:
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)
# TODO: Remove
raise e
RNS.panic()
else:
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
+3 -1
View File
@@ -585,7 +585,9 @@ class Transport:
Transport.jobs_locked = True
packet = RNS.Packet(None, raw)
packet.unpack()
if not packet.unpack():
return
packet.receiving_interface = interface
packet.hops += 1
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.3.0"
__version__ = "0.3.1"
+34 -3
View File
@@ -1,7 +1,38 @@
def get_platform():
from os import environ
if 'ANDROID_ARGUMENT' in environ:
return 'android'
if "ANDROID_ARGUMENT" in environ:
return "android"
elif "ANDROID_ROOT" in environ:
return "android"
else:
import sys
return sys.platform
return sys.platform
def is_darwin():
if get_platform() == "darwin":
return True
else:
return False
def is_android():
if get_platform() == "android":
return True
else:
return False
def is_windows():
if str(get_platform()).startswith("win"):
return True
else:
return False
def platform_checks():
if is_windows():
import sys
if sys.version_info.major >= 3 and sys.version_info.minor >= 8:
pass
else:
import RNS
RNS.log("On Windows, Reticulum requires Python 3.8 or higher.", RNS.LOG_ERROR)
RNS.log("Please update Python to run Reticulum.", RNS.LOG_ERROR)
RNS.panic()