Compare commits

...

53 Commits

Author SHA1 Message Date
Mark Qvist 469947dab9 Updated manual 2022-12-22 15:49:47 +01:00
Mark Qvist 2386fc3635 Updated documentation and manual 2022-12-22 15:11:53 +01:00
Mark Qvist e9e98a00c2 Updated version 2022-12-22 15:07:36 +01:00
Mark Qvist b305eb8e0a Improved path response handling. Prepared destination path response handling for multi-path Transport. 2022-12-22 11:28:56 +01:00
Mark Qvist dd7931d421 Added signal quality stats to announce log output 2022-12-22 11:26:59 +01:00
Mark Qvist 191dce1301 Updated manual 2022-12-20 21:13:23 +01:00
Mark Qvist 3b5a27ba60 Updated readme 2022-12-20 21:08:08 +01:00
Mark Qvist 3c91f7f18b Updated documentation 2022-12-20 20:57:49 +01:00
Mark Qvist 171457713b Improved RNode hotplug over Bluetooth on Android 2022-12-20 15:17:46 +01:00
Mark Qvist 67ee8d6aab Added originator check to path rediscovery on failed links 2022-12-19 01:31:00 +01:00
Mark Qvist 13fa7d49d9 Added automatic path rediscovery on failed link establishments 2022-12-19 01:15:49 +01:00
Mark Qvist 66d921e669 Improved resource advertisement retry handling 2022-12-19 01:10:34 +01:00
Mark Qvist 85f60ea04e Added check for already transferring resource to Link class 2022-12-19 01:04:49 +01:00
Mark Qvist 4870e741f6 Added link request proof signature validation for every transport hop 2022-12-18 21:27:14 +01:00
Mark Qvist f71c1986af Added Heltec USB issue notice to autoinstaller 2022-12-16 23:34:31 +01:00
Mark Qvist 30d8e351dd Updated version 2022-12-16 23:21:22 +01:00
Mark Qvist 5e62e3bc22 Merge branch 'master' of https://git.unsigned.io/markqvist/Reticulum 2022-12-15 21:17:16 +01:00
Mark Qvist 1a67e276ad Updated broken link. Fixes #174. Thanks @mkinney! 2022-12-15 21:16:20 +01:00
Mark Qvist df37a4a884 Updated broken link 2022-12-15 21:15:47 +01:00
Mark Qvist d26bbbd59f Merge branch 'master' of https://git.unsigned.io/markqvist/Reticulum 2022-12-15 17:14:15 +01:00
Mark Qvist 2a264fa7d6 Fixed invalid driver proxy for Qinheng CH34x chips on Android 2022-12-15 17:14:09 +01:00
Mark Qvist d5e0a461cf Fixed invalid check for None 2022-11-25 00:42:22 +01:00
Mark Qvist e28dbd4afa Updated manual 2022-11-24 17:48:04 +01:00
Mark Qvist 8626dcd69f Updated roadmap 2022-11-24 17:30:01 +01:00
Mark Qvist e34f21f4dc Updated roadmap 2022-11-24 17:29:25 +01:00
Mark Qvist f692e81b8e Fixed AutoInterface roaming on Android devices that rotate Ethernet/WiFi MAC addresses on reconnect 2022-11-24 17:19:01 +01:00
Mark Qvist 28e43b52f9 Updated manual 2022-11-24 17:16:43 +01:00
Mark Qvist 680d17fb98 Improved startup time for instances and programs connected to a shared instance 2022-11-24 13:28:22 +01:00
Mark Qvist 1e477c976c Updated documentation 2022-11-24 12:32:43 +01:00
Mark Qvist ab301cdb79 Updated version 2022-11-24 10:45:45 +01:00
Mark Qvist cecb4b3acb Fixed buffered input stream reader not working on Android API levels < 30 2022-11-23 20:39:49 +01:00
Mark Qvist de53a105a4 Improved time pretty-print function 2022-11-23 17:15:46 +01:00
Mark Qvist 9e4ae3c6fe Updated roadmap 2022-11-22 20:20:23 +01:00
Mark Qvist 3482d84bc0 Updated manual 2022-11-17 18:19:42 +01:00
Mark Qvist 51c5c85fcd Updated readme 2022-11-17 16:51:59 +01:00
Mark Qvist 57aeab43a2 Updated readme and roadmap 2022-11-17 12:39:09 +01:00
Mark Qvist 92cccddaab Updated readme and roadmap 2022-11-17 12:36:41 +01:00
Mark Qvist 3de182192a Updated readme and roadmap 2022-11-17 12:35:21 +01:00
Mark Qvist aca6b0c110 Added roadmap 2022-11-17 12:25:48 +01:00
Mark Qvist 3d6e7a9597 Updated docs 2022-11-14 11:25:47 +01:00
markqvist 21da55dd39 Merge pull request #154 from thatv/master
Fixed Hop-number in docs
2022-11-14 11:23:10 +01:00
thatv 9e664af1c6 Update understanding.html 2022-11-12 21:37:27 +01:00
Mark Qvist 7736ed589e Updated manual 2022-11-03 23:08:37 +01:00
Mark Qvist f22504d080 Improved I2P recovery time on unresponsive tunnels 2022-11-03 22:47:08 +01:00
Mark Qvist f22e5cc200 Fixed socket references. Closes #146. 2022-11-03 19:51:04 +01:00
Mark Qvist 87b73b6c67 Updated docs 2022-11-03 19:48:39 +01:00
Mark Qvist 36906f6567 Updated version 2022-11-03 18:05:13 +01:00
Mark Qvist 52edb54d21 Updated readme 2022-11-03 18:05:04 +01:00
Mark Qvist 88b88b9b64 Fixed missing check for socket state 2022-11-03 18:03:00 +01:00
Mark Qvist 76fcad0b53 Added better I2P state visibility to rnstatus util 2022-11-03 17:49:25 +01:00
Mark Qvist 01e520b082 Adjusted I2P interface timings 2022-11-03 16:30:07 +01:00
Mark Qvist 1d2a0fe4c8 Improved I2P tunnel state detection. Fixed missing IFAC init on spawned I2P interfaces. 2022-11-03 15:22:34 +01:00
Mark Qvist 0f19ced9d3 Fixed missing IFAC identity init on spawned TCP clients. Closes #137. 2022-11-03 14:16:00 +01:00
38 changed files with 610 additions and 257 deletions
+7 -33
View File
@@ -37,7 +37,7 @@ The full documentation for Reticulum is available at [markqvist.github.io/Reticu
You can also [download the Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf) You can also [download the Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf)
For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects/reticulum/) For more info, see [reticulum.network](https://reticulum.network/)
## Notable Features ## Notable Features
- Coordination-less globally unique addressing and identification - Coordination-less globally unique addressing and identification
@@ -62,6 +62,11 @@ For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects
- Total bandwidth cost of setting up an encrypted link is 3 packets totaling 297 bytes - Total bandwidth cost of setting up an encrypted link is 3 packets totaling 297 bytes
- Low cost of keeping links open at only 0.44 bits per second - Low cost of keeping links open at only 0.44 bits per second
## Development Roadmap
While Reticulum is already a fully featured and functional networking stack, many improvements and additions are actively being worked on, and planned for the future.
To learn more about the direction and future of Reticulum, please see the [Development Roadmap](./Roadmap.md).
## Examples of Reticulum Applications ## Examples of Reticulum Applications
If you want to quickly get an idea of what Reticulum can do, take a look at the If you want to quickly get an idea of what Reticulum can do, take a look at the
following resources. following resources.
@@ -177,37 +182,6 @@ features are implemented and functioning, but additions will probably occur as
real-world use is explored. There will be bugs. The API and wire-format can be real-world use is explored. There will be bugs. The API and wire-format can be
considered relatively stable at the moment, but could change if warranted. considered relatively stable at the moment, but could change if warranted.
## Development Roadmap
- Improving [the manual](https://markqvist.github.io/Reticulum/manual/) with sections specifically for beginners
- Performance and memory optimisations
- Utilities for managing identities, signing and encryption
- User friendly interface configuration tool
- More interface types for even broader compatibility
- Plain ESP32 devices (ESP-Now, WiFi, Bluetooth, etc.)
- More LoRa transceivers
- IR Transceivers
- OpenWRT support
- Metric-based path selection
- Distributed Destination Naming System
- Network-wide path balancing
- Globally routable multicast
- Bindings for other programming languages
- Multiple paths in path table for quick recovery on link failures
- A portable Reticulum implementation in C, see [#21](https://github.com/markqvist/Reticulum/discussions/21)
- Easy way to share interface configurations, see [#19](https://github.com/markqvist/Reticulum/discussions/19)
- More interface types
- AT-compatible modems
- Optical mediums
- AWDL / OWL
- HF Modems
- CAN-bus
- ZeroMQ
- MQTT
- XBee
- SPI
- i²c
- Tor
## Dependencies ## Dependencies
The installation of the default `rns` package requires the dependencies listed The installation of the default `rns` package requires the dependencies listed
below. Almost all systems and distributions have readily available packages for below. Almost all systems and distributions have readily available packages for
@@ -271,7 +245,7 @@ file:
[[RNS Testnet I2P Hub A]] [[RNS Testnet I2P Hub A]]
type = I2PInterface type = I2PInterface
enabled = yes enabled = yes
peers = mrwqlsioq4hoo2lmeeud7dkfscnm7yxak7dmiyvsrnpfag3z5tsq.b32.i2p peers = pmlm3l5rpympihoy2o5ago43kluei2jjjzsalcuiuylbve3mwi2a.b32.i2p
# Interface to I2P Hub B # Interface to I2P Hub B
[[RNS Testnet I2P Hub B]] [[RNS Testnet I2P Hub B]]
+1 -1
View File
@@ -33,7 +33,7 @@ def hkdf(length=None, derive_from=None, salt=None, context=None):
if length == None or length < 1: if length == None or length < 1:
raise ValueError("Invalid output key length") raise ValueError("Invalid output key length")
if derive_from == "None" or derive_from == "": if derive_from == None or derive_from == "":
raise ValueError("Cannot derive key from empty input material") raise ValueError("Cannot derive key from empty input material")
if salt == None or len(salt) == 0: if salt == None or len(salt) == 0:
+46 -18
View File
@@ -69,6 +69,8 @@ class Destination:
OUT = 0x12; OUT = 0x12;
directions = [IN, OUT] directions = [IN, OUT]
PR_TAG_WINDOW = 30
@staticmethod @staticmethod
def expand_name(identity, app_name, *aspects): def expand_name(identity, app_name, *aspects):
""" """
@@ -132,6 +134,7 @@ class Destination:
self.proof_strategy = Destination.PROVE_NONE self.proof_strategy = Destination.PROVE_NONE
self.mtu = 0 self.mtu = 0
self.path_responses = {}
self.links = [] self.links = []
if identity == None and direction == Destination.IN and self.type != Destination.PLAIN: if identity == None and direction == Destination.IN and self.type != Destination.PLAIN:
@@ -163,7 +166,7 @@ class Destination:
return "<"+self.name+"/"+self.hexhash+">" return "<"+self.name+"/"+self.hexhash+">"
def announce(self, app_data=None, path_response=False, send=True): def announce(self, app_data=None, path_response=False, attached_interface=None, tag=None, send=True):
""" """
Creates an announce packet for this destination and broadcasts it on all Creates an announce packet for this destination and broadcasts it on all
relevant interfaces. Application specific data can be added to the announce. relevant interfaces. Application specific data can be added to the announce.
@@ -174,34 +177,59 @@ class Destination:
if self.type != Destination.SINGLE: if self.type != Destination.SINGLE:
raise TypeError("Only SINGLE destination types can be announced") raise TypeError("Only SINGLE destination types can be announced")
destination_hash = self.hash now = time.time()
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big") stale_responses = []
for entry_tag in self.path_responses:
entry = self.path_responses[entry_tag]
if now > entry[0]+Destination.PR_TAG_WINDOW:
stale_responses.append(entry_tag)
if app_data == None and self.default_app_data != None: for entry_tag in stale_responses:
if isinstance(self.default_app_data, bytes): self.path_responses.pop(entry_tag)
app_data = self.default_app_data
elif callable(self.default_app_data):
returned_app_data = self.default_app_data()
if isinstance(returned_app_data, bytes):
app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash if (path_response == True and tag != None) and tag in self.path_responses:
if app_data != None: # This code is currently not used, since Transport will block duplicate
signed_data += app_data # path requests based on tags. When multi-path support is implemented in
# Transport, this will allow Transport to detect redundant paths to the
# same destination, and select the best one based on chosen criteria,
# since it will be able to detect that a single emitted announce was
# received via multiple paths. The difference in reception time will
# potentially also be useful in determining characteristics of the
# multiple available paths, and to choose the best one.
RNS.log("Using cached announce data for answering path request with tag "+RNS.prettyhexrep(tag), RNS.LOG_EXTREME)
announce_data = self.path_responses[tag][1]
signature = self.identity.sign(signed_data) else:
destination_hash = self.hash
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+signature if app_data == None and self.default_app_data != None:
if isinstance(self.default_app_data, bytes):
app_data = self.default_app_data
elif callable(self.default_app_data):
returned_app_data = self.default_app_data()
if isinstance(returned_app_data, bytes):
app_data = returned_app_data
if app_data != None: signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash
announce_data += app_data if app_data != None:
signed_data += app_data
signature = self.identity.sign(signed_data)
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+signature
if app_data != None:
announce_data += app_data
self.path_responses[tag] = [time.time(), announce_data]
if path_response: if path_response:
announce_context = RNS.Packet.PATH_RESPONSE announce_context = RNS.Packet.PATH_RESPONSE
else: else:
announce_context = RNS.Packet.NONE announce_context = RNS.Packet.NONE
announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context) announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, attached_interface = attached_interface)
if send: if send:
announce_packet.send() announce_packet.send()
+15 -3
View File
@@ -255,10 +255,22 @@ class Identity:
RNS.Identity.remember(packet.get_hash(), destination_hash, public_key, app_data) RNS.Identity.remember(packet.get_hash(), destination_hash, public_key, app_data)
del announced_identity del announced_identity
if hasattr(packet, "transport_id") and packet.transport_id != None: if packet.rssi != None or packet.snr != None:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received via "+RNS.prettyhexrep(packet.transport_id)+" on "+str(packet.receiving_interface), RNS.LOG_EXTREME) signal_str = " ["
if packet.rssi != None:
signal_str += "RSSI "+str(packet.rssi)+"dBm"
if packet.snr != None:
signal_str += ", "
if packet.snr != None:
signal_str += "SNR "+str(packet.snr)+"dB"
signal_str += "]"
else: else:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface), RNS.LOG_EXTREME) signal_str = ""
if hasattr(packet, "transport_id") and packet.transport_id != None:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received via "+RNS.prettyhexrep(packet.transport_id)+" on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME)
else:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME)
return True return True
+26 -11
View File
@@ -193,7 +193,12 @@ class AndroidBluetoothManager():
if self.connected and self.rfcomm_reader != None: if self.connected and self.rfcomm_reader != None:
available = self.rfcomm_reader.available() available = self.rfcomm_reader.available()
if available > 0: if available > 0:
return self.rfcomm_reader.readNBytes(available) if hasattr(self.rfcomm_reader, "readNBytes"):
return self.rfcomm_reader.readNBytes(available)
else:
# Compatibility mode for older android versions lacking readNBytes
rb = self.rfcomm_reader.read().to_bytes(1, "big")
return rb
else: else:
return bytes([]) return bytes([])
else: else:
@@ -249,13 +254,14 @@ class RNodeInterface(Interface):
# Driver overrides for speficic chips # Driver overrides for speficic chips
from usbserial4a import serial4a as pyserial from usbserial4a import serial4a as pyserial
proxy = pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # Force CDC driver for Qinheng CH34x
RNS.log("Using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG) RNS.log("Using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
serial = pyserial.get_serial_port( serial = proxy(
port, port,
baudrate = 115200, baudrate = 115200,
bytesize = 8, bytesize = 8,
@@ -323,11 +329,13 @@ class RNodeInterface(Interface):
from usbserial4a import serial4a as serial from usbserial4a import serial4a as serial
self.parity = "N" self.parity = "N"
self.bt_target_device_name = target_device_name
self.bt_target_device_address = target_device_address
if allow_bluetooth: if allow_bluetooth:
self.bt_manager = AndroidBluetoothManager( self.bt_manager = AndroidBluetoothManager(
owner = self, owner = self,
target_device_name = target_device_name, target_device_name = self.bt_target_device_name,
target_device_address = target_device_address target_device_address = self.bt_target_device_address
) )
else: else:
@@ -538,6 +546,13 @@ class RNodeInterface(Interface):
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
elif self.allow_bluetooth: elif self.allow_bluetooth:
if self.bt_manager == None:
self.bt_manager = AndroidBluetoothManager(
owner = self,
target_device_name = self.bt_target_device_name,
target_device_address = self.bt_target_device_address
)
if self.bt_manager != None: if self.bt_manager != None:
self.bt_manager.connect_any_device() self.bt_manager.connect_any_device()
@@ -549,7 +564,7 @@ class RNodeInterface(Interface):
thread.start() thread.start()
self.detect() self.detect()
sleep(0.4) sleep(0.5)
if not self.detected: if not self.detected:
raise IOError("Could not detect device") raise IOError("Could not detect device")
@@ -587,22 +602,22 @@ class RNodeInterface(Interface):
def initRadio(self): def initRadio(self):
self.setFrequency() self.setFrequency()
time.sleep(0.1) time.sleep(0.15)
self.setBandwidth() self.setBandwidth()
time.sleep(0.1) time.sleep(0.15)
self.setTXPower() self.setTXPower()
time.sleep(0.1) time.sleep(0.15)
self.setSpreadingFactor() self.setSpreadingFactor()
time.sleep(0.1) time.sleep(0.15)
self.setCodingRate() self.setCodingRate()
time.sleep(0.1) time.sleep(0.15)
self.setRadioState(KISS.RADIO_STATE_ON) self.setRadioState(KISS.RADIO_STATE_ON)
time.sleep(0.1) time.sleep(0.15)
def detect(self): 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]) 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])
+33 -7
View File
@@ -48,6 +48,11 @@ class AutoInterface(Interface):
BITRATE_GUESS = 10*1000*1000 BITRATE_GUESS = 10*1000*1000
def handler_factory(self, callback):
def create_handler(*args, **keys):
return AutoInterfaceHandler(callback, *args, **keys)
return create_handler
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None): def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
import importlib import importlib
if importlib.util.find_spec('netifaces') != None: if importlib.util.find_spec('netifaces') != None:
@@ -70,6 +75,7 @@ class AutoInterface(Interface):
self.peers = {} self.peers = {}
self.link_local_addresses = [] self.link_local_addresses = []
self.adopted_interfaces = {} self.adopted_interfaces = {}
self.interface_servers = {}
self.multicast_echoes = {} self.multicast_echoes = {}
self.timed_out_interfaces = {} self.timed_out_interfaces = {}
@@ -199,11 +205,6 @@ class AutoInterface(Interface):
peering_wait = self.announce_interval*1.2 peering_wait = self.announce_interval*1.2
RNS.log(str(self)+" discovering peers for "+str(round(peering_wait, 2))+" seconds...", RNS.LOG_VERBOSE) RNS.log(str(self)+" discovering peers for "+str(round(peering_wait, 2))+" seconds...", RNS.LOG_VERBOSE)
def handlerFactory(callback):
def createHandler(*args, **keys):
return AutoInterfaceHandler(callback, *args, **keys)
return createHandler
self.owner = owner self.owner = owner
socketserver.UDPServer.address_family = socket.AF_INET6 socketserver.UDPServer.address_family = socket.AF_INET6
@@ -212,9 +213,10 @@ class AutoInterface(Interface):
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM) addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
address = addr_info[0][4] address = addr_info[0][4]
self.server = socketserver.UDPServer(address, handlerFactory(self.processIncoming)) udp_server = socketserver.UDPServer(address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server
thread = threading.Thread(target=self.server.serve_forever) thread = threading.Thread(target=udp_server.serve_forever)
thread.daemon = True thread.daemon = True
thread.start() thread.start()
@@ -277,8 +279,32 @@ class AutoInterface(Interface):
if address["addr"].startswith("fe80:"): if address["addr"].startswith("fe80:"):
link_local_addr = address["addr"].split("%")[0] link_local_addr = address["addr"].split("%")[0]
if link_local_addr != self.adopted_interfaces[ifname]: if link_local_addr != self.adopted_interfaces[ifname]:
# TODO: Remove
# RNS.log("Replacing link-local address for "+str(ifname), RNS.LOG_DEBUG)
self.adopted_interfaces[ifname] = link_local_addr self.adopted_interfaces[ifname] = link_local_addr
local_addr = link_local_addr+"%"+ifname
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
listen_address = addr_info[0][4]
if ifname in self.interface_servers:
# TODO: Remove
# RNS.log("Shutting down previous UDP socket server for "+str(ifname), RNS.LOG_DEBUG)
previous_server = self.interface_servers[ifname]
def shutdown_server():
previous_server.shutdown()
threading.Thread(target=shutdown_server, daemon=True).start()
# TODO: Remove
# RNS.log("Starting new UDP socket server for "+str(ifname), RNS.LOG_DEBUG)
udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server
thread = threading.Thread(target=udp_server.serve_forever)
thread.daemon = True
thread.start()
except Exception as e: except Exception as e:
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
+125 -27
View File
@@ -161,8 +161,6 @@ class I2PController:
raise tn.status["exception"] raise tn.status["exception"]
else: else:
self.client_tunnels[i2p_destination] = True
owner.awaiting_i2p_tunnel = False
if owner.socket != None: if owner.socket != None:
if hasattr(owner.socket, "close"): if hasattr(owner.socket, "close"):
if callable(owner.socket.close): if callable(owner.socket.close):
@@ -175,6 +173,8 @@ class I2PController:
owner.socket.close() owner.socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(owner)+": "+str(e)) RNS.log("Error while closing socket for "+str(owner)+": "+str(e))
self.client_tunnels[i2p_destination] = True
owner.awaiting_i2p_tunnel = False
RNS.log(str(owner)+" tunnel setup complete", RNS.LOG_VERBOSE) RNS.log(str(owner)+" tunnel setup complete", RNS.LOG_VERBOSE)
@@ -383,6 +383,11 @@ class I2PInterfacePeer(Interface):
I2P_PROBE_AFTER = 10 I2P_PROBE_AFTER = 10
I2P_PROBE_INTERVAL = 9 I2P_PROBE_INTERVAL = 9
I2P_PROBES = 5 I2P_PROBES = 5
I2P_READ_TIMEOUT = (I2P_PROBE_INTERVAL * I2P_PROBES + I2P_PROBE_AFTER)*2
TUNNEL_STATE_INIT = 0x00
TUNNEL_STATE_ACTIVE = 0x01
TUNNEL_STATE_STALE = 0x02
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None): def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
self.rxb = 0 self.rxb = 0
@@ -409,6 +414,30 @@ class I2PInterfacePeer(Interface):
self.i2p_tunnel_ready = False self.i2p_tunnel_ready = False
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
self.bitrate = I2PInterface.BITRATE_GUESS self.bitrate = I2PInterface.BITRATE_GUESS
self.last_read = 0
self.last_write = 0
self.wd_reset = False
self.i2p_tunnel_state = I2PInterfacePeer.TUNNEL_STATE_INIT
self.ifac_size = self.parent_interface.ifac_size
self.ifac_netname = self.parent_interface.ifac_netname
self.ifac_netkey = self.parent_interface.ifac_netkey
if self.ifac_netname != None or self.ifac_netkey != None:
ifac_origin = b""
if self.ifac_netname != None:
ifac_origin += RNS.Identity.full_hash(self.ifac_netname.encode("utf-8"))
if self.ifac_netkey != None:
ifac_origin += RNS.Identity.full_hash(self.ifac_netkey.encode("utf-8"))
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
self.ifac_key = RNS.Cryptography.hkdf(
length=64,
derive_from=ifac_origin_hash,
salt=RNS.Reticulum.IFAC_SALT,
context=None
)
self.ifac_identity = RNS.Identity.from_bytes(self.ifac_key)
self.ifac_signature = self.ifac_identity.sign(RNS.Identity.full_hash(self.ifac_key))
self.announce_rate_target = None self.announce_rate_target = None
self.announce_rate_grace = None self.announce_rate_grace = None
@@ -454,7 +483,7 @@ class I2PInterfacePeer(Interface):
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR) RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR)
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR) RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(15) time.sleep(8)
thread = threading.Thread(target=tunnel_job) thread = threading.Thread(target=tunnel_job)
thread.daemon = True thread.daemon = True
@@ -463,6 +492,7 @@ class I2PInterfacePeer(Interface):
def wait_job(): def wait_job():
while self.awaiting_i2p_tunnel: while self.awaiting_i2p_tunnel:
time.sleep(0.25) time.sleep(0.25)
time.sleep(2)
if not self.kiss_framing: if not self.kiss_framing:
self.wants_tunnel = True self.wants_tunnel = True
@@ -482,18 +512,11 @@ class I2PInterfacePeer(Interface):
def set_timeouts_linux(self): def set_timeouts_linux(self):
if not self.i2p_tunneled: self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(I2PInterfacePeer.I2P_USER_TIMEOUT * 1000))
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(I2PInterfacePeer.TCP_USER_TIMEOUT * 1000)) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(I2PInterfacePeer.TCP_PROBE_AFTER)) self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(I2PInterfacePeer.I2P_PROBE_INTERVAL))
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(I2PInterfacePeer.TCP_PROBE_INTERVAL)) self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(I2PInterfacePeer.I2P_PROBES))
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(I2PInterfacePeer.TCP_PROBES))
else:
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(I2PInterfacePeer.I2P_USER_TIMEOUT * 1000))
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(I2PInterfacePeer.I2P_PROBE_INTERVAL))
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(I2PInterfacePeer.I2P_PROBES))
def set_timeouts_osx(self): def set_timeouts_osx(self):
if hasattr(socket, "TCP_KEEPALIVE"): if hasattr(socket, "TCP_KEEPALIVE"):
@@ -502,22 +525,19 @@ class I2PInterfacePeer(Interface):
TCP_KEEPIDLE = 0x10 TCP_KEEPIDLE = 0x10
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
if not self.i2p_tunneled: def shutdown_socket(self, target_socket):
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.TCP_PROBE_AFTER)) if callable(target_socket.close):
else:
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
def shutdown_socket(self, socket):
if callable(socket.close):
try: try:
socket.shutdown(socket.SHUT_RDWR) if socket != None:
target_socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
try: try:
socket.close() if socket != None:
target_socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e)) RNS.log("Error while closing socket for "+str(self)+": "+str(e))
@@ -571,7 +591,6 @@ class I2PInterfacePeer(Interface):
return True return True
def reconnect(self): def reconnect(self):
if self.initiator: if self.initiator:
if not self.reconnecting: if not self.reconnecting:
@@ -632,6 +651,7 @@ class I2PInterfacePeer(Interface):
self.socket.sendall(data) self.socket.sendall(data)
self.writing = False self.writing = False
self.txb += len(data) self.txb += len(data)
self.last_write = time.time()
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count: if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
@@ -642,8 +662,59 @@ class I2PInterfacePeer(Interface):
self.teardown() self.teardown()
def read_watchdog(self):
while self.wd_reset:
time.sleep(0.25)
should_run = True
try:
while should_run and not self.wd_reset:
time.sleep(1)
if (time.time()-self.last_read > I2PInterfacePeer.I2P_PROBE_AFTER*2):
if self.i2p_tunnel_state != I2PInterfacePeer.TUNNEL_STATE_STALE:
RNS.log("I2P tunnel became unresponsive", RNS.LOG_DEBUG)
self.i2p_tunnel_state = I2PInterfacePeer.TUNNEL_STATE_STALE
else:
self.i2p_tunnel_state = I2PInterfacePeer.TUNNEL_STATE_ACTIVE
if (time.time()-self.last_write > I2PInterfacePeer.I2P_PROBE_AFTER*1):
try:
if self.socket != None:
self.socket.sendall(bytes([HDLC.FLAG, HDLC.FLAG]))
except Exception as e:
RNS.log("An error ocurred while sending I2P keepalive. The contained exception was: "+str(e), RNS.LOG_ERROR)
self.shutdown_socket(self.socket)
should_run = False
if (time.time()-self.last_read > I2PInterfacePeer.I2P_READ_TIMEOUT):
RNS.log("I2P socket is unresponsive, restarting...", RNS.LOG_WARNING)
if self.socket != None:
try:
self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
try:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
should_run = False
self.wd_reset = False
finally:
self.wd_reset = False
def read_loop(self): def read_loop(self):
try: try:
self.last_read = time.time()
self.last_write = time.time()
wd_thread = threading.Thread(target=self.read_watchdog, daemon=True).start()
in_frame = False in_frame = False
escape = False escape = False
data_buffer = b"" data_buffer = b""
@@ -653,6 +724,7 @@ class I2PInterfacePeer(Interface):
data_in = self.socket.recv(4096) data_in = self.socket.recv(4096)
if len(data_in) > 0: if len(data_in) > 0:
pointer = 0 pointer = 0
self.last_read = time.time()
while pointer < len(data_in): while pointer < len(data_in):
byte = data_in[pointer] byte = data_in[pointer]
pointer += 1 pointer += 1
@@ -705,6 +777,11 @@ class I2PInterfacePeer(Interface):
data_buffer = data_buffer+bytes([byte]) data_buffer = data_buffer+bytes([byte])
else: else:
self.online = False self.online = False
self.wd_reset = True
time.sleep(2)
self.wd_reset = False
if self.initiator and not self.detached: if self.initiator and not self.detached:
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING) RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
self.reconnect() self.reconnect()
@@ -754,7 +831,7 @@ class I2PInterfacePeer(Interface):
class I2PInterface(Interface): class I2PInterface(Interface):
BITRATE_GUESS = 256*1000 BITRATE_GUESS = 256*1000
def __init__(self, owner, name, rns_storagepath, peers, connectable = False): def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
self.rxb = 0 self.rxb = 0
self.txb = 0 self.txb = 0
@@ -780,6 +857,9 @@ class I2PInterface(Interface):
self.bind_port = self.i2p.get_free_port() self.bind_port = self.i2p.get_free_port()
self.address = (self.bind_ip, self.bind_port) self.address = (self.bind_ip, self.bind_port)
self.bitrate = I2PInterface.BITRATE_GUESS self.bitrate = I2PInterface.BITRATE_GUESS
self.ifac_size = ifac_size
self.ifac_netname = ifac_netname
self.ifac_netkey = ifac_netkey
self.online = False self.online = False
@@ -850,9 +930,27 @@ class I2PInterface(Interface):
spawned_interface.parent_interface = self spawned_interface.parent_interface = self
spawned_interface.online = True spawned_interface.online = True
spawned_interface.bitrate = self.bitrate spawned_interface.bitrate = self.bitrate
spawned_interface.ifac_size = self.ifac_size spawned_interface.ifac_size = self.ifac_size
spawned_interface.ifac_netname = self.ifac_netname spawned_interface.ifac_netname = self.ifac_netname
spawned_interface.ifac_netkey = self.ifac_netkey spawned_interface.ifac_netkey = self.ifac_netkey
if spawned_interface.ifac_netname != None or spawned_interface.ifac_netkey != None:
ifac_origin = b""
if spawned_interface.ifac_netname != None:
ifac_origin += RNS.Identity.full_hash(spawned_interface.ifac_netname.encode("utf-8"))
if spawned_interface.ifac_netkey != None:
ifac_origin += RNS.Identity.full_hash(spawned_interface.ifac_netkey.encode("utf-8"))
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
spawned_interface.ifac_key = RNS.Cryptography.hkdf(
length=64,
derive_from=ifac_origin_hash,
salt=RNS.Reticulum.IFAC_SALT,
context=None
)
spawned_interface.ifac_identity = RNS.Identity.from_bytes(spawned_interface.ifac_key)
spawned_interface.ifac_signature = spawned_interface.ifac_identity.sign(RNS.Identity.full_hash(spawned_interface.ifac_key))
spawned_interface.announce_rate_target = self.announce_rate_target spawned_interface.announce_rate_target = self.announce_rate_target
spawned_interface.announce_rate_grace = self.announce_rate_grace spawned_interface.announce_rate_grace = self.announce_rate_grace
spawned_interface.announce_rate_penalty = self.announce_rate_penalty spawned_interface.announce_rate_penalty = self.announce_rate_penalty
+18
View File
@@ -483,9 +483,27 @@ class TCPServerInterface(Interface):
spawned_interface.target_port = str(handler.client_address[1]) spawned_interface.target_port = str(handler.client_address[1])
spawned_interface.parent_interface = self spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate spawned_interface.bitrate = self.bitrate
spawned_interface.ifac_size = self.ifac_size spawned_interface.ifac_size = self.ifac_size
spawned_interface.ifac_netname = self.ifac_netname spawned_interface.ifac_netname = self.ifac_netname
spawned_interface.ifac_netkey = self.ifac_netkey spawned_interface.ifac_netkey = self.ifac_netkey
if spawned_interface.ifac_netname != None or spawned_interface.ifac_netkey != None:
ifac_origin = b""
if spawned_interface.ifac_netname != None:
ifac_origin += RNS.Identity.full_hash(spawned_interface.ifac_netname.encode("utf-8"))
if spawned_interface.ifac_netkey != None:
ifac_origin += RNS.Identity.full_hash(spawned_interface.ifac_netkey.encode("utf-8"))
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
spawned_interface.ifac_key = RNS.Cryptography.hkdf(
length=64,
derive_from=ifac_origin_hash,
salt=RNS.Reticulum.IFAC_SALT,
context=None
)
spawned_interface.ifac_identity = RNS.Identity.from_bytes(spawned_interface.ifac_key)
spawned_interface.ifac_signature = spawned_interface.ifac_identity.sign(RNS.Identity.full_hash(spawned_interface.ifac_key))
spawned_interface.announce_rate_target = self.announce_rate_target spawned_interface.announce_rate_target = self.announce_rate_target
spawned_interface.announce_rate_grace = self.announce_rate_grace spawned_interface.announce_rate_grace = self.announce_rate_grace
spawned_interface.announce_rate_penalty = self.announce_rate_penalty spawned_interface.announce_rate_penalty = self.announce_rate_penalty
+7
View File
@@ -900,6 +900,13 @@ class Link:
def register_incoming_resource(self, resource): def register_incoming_resource(self, resource):
self.incoming_resources.append(resource) self.incoming_resources.append(resource)
def has_incoming_resource(self, resource):
for incoming_resource in self.incoming_resources:
if incoming_resource.hash == resource.hash:
return True
return False
def cancel_outgoing_resource(self, resource): def cancel_outgoing_resource(self, resource):
if resource in self.outgoing_resources: if resource in self.outgoing_resources:
self.outgoing_resources.remove(resource) self.outgoing_resources.remove(resource)
+19 -13
View File
@@ -172,20 +172,26 @@ class Resource:
resource.consecutive_completed_height = 0 resource.consecutive_completed_height = 0
resource.link.register_incoming_resource(resource) if not resource.link.has_incoming_resource(resource):
resource.link.register_incoming_resource(resource)
RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG) RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
if resource.link.callbacks.resource_started != None: if resource.link.callbacks.resource_started != None:
try: try:
resource.link.callbacks.resource_started(resource) resource.link.callbacks.resource_started(resource)
except Exception as e: except Exception as e:
RNS.log("Error while executing resource started callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Error while executing resource started callback from "+str(resource)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
resource.hashmap_update(0, resource.hashmap_raw) resource.hashmap_update(0, resource.hashmap_raw)
resource.watchdog_job() resource.watchdog_job()
return resource
else:
RNS.log("Ignoring resource advertisement for "+RNS.prettyhexrep(resource.hash)+", resource already transferring", RNS.LOG_DEBUG)
return None
return resource
except Exception as e: except Exception as e:
RNS.log("Could not decode resource advertisement, dropping resource", RNS.LOG_DEBUG) RNS.log("Could not decode resource advertisement, dropping resource", RNS.LOG_DEBUG)
return None return None
@@ -397,8 +403,7 @@ class Resource:
thread.start() thread.start()
def __advertise_job(self): def __advertise_job(self):
data = ResourceAdvertisement(self).pack() self.advertisement_packet = RNS.Packet(self.link, ResourceAdvertisement(self).pack(), context=RNS.Packet.RESOURCE_ADV)
self.advertisement_packet = RNS.Packet(self.link, data, context=RNS.Packet.RESOURCE_ADV)
while not self.link.ready_for_new_resource(): while not self.link.ready_for_new_resource():
self.status = Resource.QUEUED self.status = Resource.QUEUED
sleep(0.25) sleep(0.25)
@@ -445,7 +450,8 @@ class Resource:
try: try:
RNS.log("No part requests received, retrying resource advertisement...", RNS.LOG_DEBUG) RNS.log("No part requests received, retrying resource advertisement...", RNS.LOG_DEBUG)
self.retries_left -= 1 self.retries_left -= 1
self.advertisement_packet.resend() self.advertisement_packet = RNS.Packet(self.link, ResourceAdvertisement(self).pack(), context=RNS.Packet.RESOURCE_ADV)
self.advertisement_packet.send()
self.last_activity = time.time() self.last_activity = time.time()
self.adv_sent = self.last_activity self.adv_sent = self.last_activity
sleep_time = 0.001 sleep_time = 0.001
+19 -4
View File
@@ -631,12 +631,18 @@ class Reticulum:
i2p_peers = c.as_list("peers") if "peers" in c else None i2p_peers = c.as_list("peers") if "peers" in c else None
connectable = c.as_bool("connectable") if "connectable" in c else False connectable = c.as_bool("connectable") if "connectable" in c else False
if ifac_size == None:
ifac_size = 16
interface = I2PInterface.I2PInterface( interface = I2PInterface.I2PInterface(
RNS.Transport, RNS.Transport,
name, name,
Reticulum.storagepath, Reticulum.storagepath,
i2p_peers, i2p_peers,
connectable = connectable, connectable = connectable,
ifac_size = ifac_size,
ifac_netname = ifac_netname,
ifac_netkey = ifac_netkey,
) )
if "outgoing" in c and c.as_bool("outgoing") == False: if "outgoing" in c and c.as_bool("outgoing") == False:
@@ -653,10 +659,6 @@ class Reticulum:
interface.announce_cap = announce_cap interface.announce_cap = announce_cap
if configured_bitrate: if configured_bitrate:
interface.bitrate = configured_bitrate interface.bitrate = configured_bitrate
if ifac_size != None:
interface.ifac_size = ifac_size
else:
interface.ifac_size = 16
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
@@ -1071,6 +1073,19 @@ class Reticulum:
else: else:
ifstats["i2p_b32"] = None ifstats["i2p_b32"] = None
if hasattr(interface, "i2p_tunnel_state"):
if interface.i2p_tunnel_state != None:
state_description = "Unknown State"
if interface.i2p_tunnel_state == I2PInterface.I2PInterfacePeer.TUNNEL_STATE_ACTIVE:
state_description = "Tunnel Active"
elif interface.i2p_tunnel_state == I2PInterface.I2PInterfacePeer.TUNNEL_STATE_INIT:
state_description = "Creating Tunnel"
elif interface.i2p_tunnel_state == I2PInterface.I2PInterfacePeer.TUNNEL_STATE_STALE:
state_description = "Tunnel Unresponsive"
ifstats["tunnelstate"] = state_description
else:
ifstats["tunnelstate"] = None
if hasattr(interface, "bitrate"): if hasattr(interface, "bitrate"):
if interface.bitrate != None: if interface.bitrate != None:
ifstats["bitrate"] = interface.bitrate ifstats["bitrate"] = interface.bitrate
+106 -58
View File
@@ -66,6 +66,7 @@ class Transport:
PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds
PATH_REQUEST_GRACE = 0.35 # Grace time before a path announcement is made, allows directly reachable peers to respond first 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 PATH_REQUEST_RW = 2 # Path request random window
PATH_REQUEST_MI = 5 # Minimum interval in seconds for automated path requests
LINK_TIMEOUT = RNS.Link.STALE_TIME * 1.25 LINK_TIMEOUT = RNS.Link.STALE_TIME * 1.25
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after 30 minutes REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after 30 minutes
@@ -92,6 +93,7 @@ class Transport:
announce_handlers = [] # A table storing externally registered announce handlers announce_handlers = [] # A table storing externally registered announce handlers
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
path_requests = {} # A table for storing path request timestamps
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
discovery_pr_tags = [] # A table for keeping track of tagged path requests discovery_pr_tags = [] # A table for keeping track of tagged path requests
@@ -144,13 +146,14 @@ class Transport:
RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE) RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE)
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist" packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
if os.path.isfile(packet_hashlist_path): if not Transport.owner.is_connected_to_shared_instance:
try: if os.path.isfile(packet_hashlist_path):
file = open(packet_hashlist_path, "rb") try:
Transport.packet_hashlist = umsgpack.unpackb(file.read()) file = open(packet_hashlist_path, "rb")
file.close() Transport.packet_hashlist = umsgpack.unpackb(file.read())
except Exception as e: file.close()
RNS.log("Could not load packet hashlist from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) except Exception as e:
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
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
@@ -284,6 +287,7 @@ class Transport:
@staticmethod @staticmethod
def jobs(): def jobs():
outgoing = [] outgoing = []
path_requests = []
Transport.jobs_running = True Transport.jobs_running = True
try: try:
@@ -379,8 +383,24 @@ class Transport:
stale_links = [] stale_links = []
for link_id in Transport.link_table: for link_id in Transport.link_table:
link_entry = Transport.link_table[link_id] link_entry = Transport.link_table[link_id]
if time.time() > link_entry[0] + Transport.LINK_TIMEOUT: if link_entry[7] == True:
stale_links.append(link_id) if time.time() > link_entry[0] + Transport.LINK_TIMEOUT:
stale_links.append(link_id)
else:
if time.time() > link_entry[8]:
stale_links.append(link_id)
last_path_request = 0
if link_entry[6] in Transport.path_requests:
last_path_request = Transport.path_requests[link_entry[6]]
# If this link request was originated from this instance
# or a local client, attempt to rediscover a path to the
# destination, if it has not already happened recently.
lr_taken_hops = link_entry[5]
if lr_taken_hops == 0 and time.time() - last_path_request > Transport.PATH_REQUEST_MI:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established", RNS.LOG_DEBUG)
path_requests.append(link_entry[6])
# Cull the path table # Cull the path table
stale_paths = [] stale_paths = []
@@ -512,6 +532,9 @@ class Transport:
for packet in outgoing: for packet in outgoing:
packet.send() packet.send()
for destination_hash in path_requests:
Transport.request_path(destination_hash)
@staticmethod @staticmethod
def transmit(interface, raw): def transmit(interface, raw):
try: try:
@@ -833,7 +856,7 @@ class Transport:
def inbound(raw, interface=None): def inbound(raw, interface=None):
# If interface access codes are enabled, # If interface access codes are enabled,
# we must authenticate each packet. # we must authenticate each packet.
if len(raw) > 1: if len(raw) > 2:
if interface != None and hasattr(interface, "ifac_identity") and interface.ifac_identity != None: if interface != None and hasattr(interface, "ifac_identity") and interface.ifac_identity != None:
# Check that IFAC flag is set # Check that IFAC flag is set
if raw[0] & 0x80 == 0x80: if raw[0] & 0x80 == 0x80:
@@ -871,6 +894,9 @@ class Transport:
# If the flag is set, drop the packet # If the flag is set, drop the packet
return return
else:
return
while (Transport.jobs_running): while (Transport.jobs_running):
sleep(0.0005) sleep(0.0005)
@@ -994,15 +1020,19 @@ class Transport:
outbound_interface = Transport.destination_table[packet.destination_hash][5] outbound_interface = Transport.destination_table[packet.destination_hash][5]
if packet.packet_type == RNS.Packet.LINKREQUEST: if packet.packet_type == RNS.Packet.LINKREQUEST:
now = time.time()
proof_timeout = now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops)
# Entry format is # Entry format is
link_entry = [ time.time(), # 0: Timestamp, link_entry = [ now, # 0: Timestamp,
next_hop, # 1: Next-hop transport ID next_hop, # 1: Next-hop transport ID
outbound_interface, # 2: Next-hop interface outbound_interface, # 2: Next-hop interface
remaining_hops, # 3: Remaining hops remaining_hops, # 3: Remaining hops
packet.receiving_interface, # 4: Received on interface packet.receiving_interface, # 4: Received on interface
packet.hops, # 5: Taken hops packet.hops, # 5: Taken hops
packet.destination_hash, # 6: Original destination hash packet.destination_hash, # 6: Original destination hash
False] # 7: Validated False, # 7: Validated
proof_timeout] # 8: Proof timeout timestamp
Transport.link_table[packet.getTruncatedHash()] = link_entry Transport.link_table[packet.getTruncatedHash()] = link_entry
@@ -1202,9 +1232,9 @@ class Transport:
retransmit_timeout = now + (RNS.rand() * Transport.PATHFINDER_RW) retransmit_timeout = now + (RNS.rand() * Transport.PATHFINDER_RW)
if packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT: if hasattr(packet.receiving_interface, "mode") and packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
expires = now + Transport.AP_PATH_TIME expires = now + Transport.AP_PATH_TIME
elif packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING: elif hasattr(packet.receiving_interface, "mode") and packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
expires = now + Transport.ROAMING_PATH_TIME expires = now + Transport.ROAMING_PATH_TIME
else: else:
expires = now + Transport.PATHFINDER_E expires = now + Transport.PATHFINDER_E
@@ -1377,10 +1407,11 @@ class Transport:
# Handling for linkrequests to local destinations # Handling for linkrequests to local destinations
elif packet.packet_type == RNS.Packet.LINKREQUEST: elif packet.packet_type == RNS.Packet.LINKREQUEST:
for destination in Transport.destinations: if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
if destination.hash == packet.destination_hash and destination.type == packet.destination_type: for destination in Transport.destinations:
packet.destination = destination if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
destination.receive(packet) packet.destination = destination
destination.receive(packet)
# Handling for local data packets # Handling for local data packets
elif packet.packet_type == RNS.Packet.DATA: elif packet.packet_type == RNS.Packet.DATA:
@@ -1414,14 +1445,29 @@ class Transport:
if (RNS.Reticulum.transport_enabled() or for_local_client_link or from_local_client) and packet.destination_hash in Transport.link_table: if (RNS.Reticulum.transport_enabled() or for_local_client_link or from_local_client) and packet.destination_hash in Transport.link_table:
link_entry = Transport.link_table[packet.destination_hash] link_entry = Transport.link_table[packet.destination_hash]
if packet.receiving_interface == link_entry[2]: if packet.receiving_interface == link_entry[2]:
# TODO: Should we validate the LR proof at each transport try:
# step before transporting it? if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
# RNS.log("Link request proof received on correct interface, transporting it via "+str(link_entry[4]), RNS.LOG_EXTREME) peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
new_raw = packet.raw[0:1] peer_identity = RNS.Identity.recall(link_entry[6])
new_raw += struct.pack("!B", packet.hops) peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
new_raw += packet.raw[2:]
Transport.link_table[packet.destination_hash][7] = True signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
Transport.transmit(link_entry[4], new_raw) signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if peer_identity.validate(signature, signed_data):
RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
Transport.link_table[packet.destination_hash][7] = True
Transport.transmit(link_entry[4], new_raw)
else:
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
else: else:
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
else: else:
@@ -1825,6 +1871,7 @@ class Transport:
on_interface.announce_allowed_at = now + wait_time on_interface.announce_allowed_at = now + wait_time
packet.send() packet.send()
Transport.path_requests[destination_hash] = time.time()
@staticmethod @staticmethod
def path_request_handler(data, packet): def path_request_handler(data, packet):
@@ -1901,10 +1948,9 @@ class Transport:
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None) local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
if local_destination != None: if local_destination != None:
local_destination.announce(path_response=True) local_destination.announce(path_response=True, tag=tag, attached_interface=attached_interface)
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", destination is local to this system", RNS.LOG_DEBUG) RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", destination is local to this system", RNS.LOG_DEBUG)
elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table): elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table):
packet = Transport.destination_table[destination_hash][6] packet = Transport.destination_table[destination_hash][6]
next_hop = Transport.destination_table[destination_hash][1] next_hop = Transport.destination_table[destination_hash][1]
@@ -1949,9 +1995,10 @@ class Transport:
# Forward path request on all interfaces # Forward path request on all interfaces
# except the local client # except the local client
RNS.log("Forwarding path request from local client for "+RNS.prettyhexrep(destination_hash)+interface_str+" to all other interfaces", RNS.LOG_DEBUG) RNS.log("Forwarding path request from local client for "+RNS.prettyhexrep(destination_hash)+interface_str+" to all other interfaces", RNS.LOG_DEBUG)
request_tag = RNS.Identity.get_random_hash()
for interface in Transport.interfaces: for interface in Transport.interfaces:
if not interface == attached_interface: if not interface == attached_interface:
Transport.request_path(destination_hash, interface) Transport.request_path(destination_hash, interface, tag = request_tag)
elif should_search_for_unknown: elif should_search_for_unknown:
if destination_hash in Transport.discovery_path_requests: if destination_hash in Transport.discovery_path_requests:
@@ -2078,41 +2125,42 @@ class Transport:
@staticmethod @staticmethod
def save_packet_hashlist(): def save_packet_hashlist():
if hasattr(Transport, "saving_packet_hashlist"): if not Transport.owner.is_connected_to_shared_instance:
wait_interval = 0.2 if hasattr(Transport, "saving_packet_hashlist"):
wait_timeout = 5 wait_interval = 0.2
wait_start = time.time() wait_timeout = 5
while Transport.saving_packet_hashlist: wait_start = time.time()
time.sleep(wait_interval) while Transport.saving_packet_hashlist:
if time.time() > wait_start+wait_timeout: time.sleep(wait_interval)
RNS.log("Could not save packet hashlist to storage, waiting for previous save operation timed out.", RNS.LOG_ERROR) if time.time() > wait_start+wait_timeout:
return False RNS.log("Could not save packet hashlist to storage, waiting for previous save operation timed out.", RNS.LOG_ERROR)
return False
try: try:
Transport.saving_packet_hashlist = True Transport.saving_packet_hashlist = True
save_start = time.time() save_start = time.time()
if not RNS.Reticulum.transport_enabled(): if not RNS.Reticulum.transport_enabled():
Transport.packet_hashlist = [] Transport.packet_hashlist = []
else: else:
RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG) RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG)
packet_hashlist_path = RNS.Reticulum.storagepath+"/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()
save_time = time.time() - save_start save_time = time.time() - save_start
if save_time < 1: if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms" time_str = str(round(save_time*1000,2))+"ms"
else: else:
time_str = str(round(save_time,2))+"s" time_str = str(round(save_time,2))+"s"
RNS.log("Saved packet hashlist in "+time_str, RNS.LOG_DEBUG) RNS.log("Saved packet hashlist in "+time_str, RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Could not save packet hashlist to storage, 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)
Transport.saving_packet_hashlist = False Transport.saving_packet_hashlist = False
@staticmethod @staticmethod
+6 -1
View File
@@ -1073,7 +1073,7 @@ def main():
print(" ["+str(pi)+"] "+str(port.device)+" ("+str(port.product)+", "+str(port.serial_number)+")") print(" ["+str(pi)+"] "+str(port.device)+" ("+str(port.product)+", "+str(port.serial_number)+")")
pi += 1 pi += 1
print("\nWhat serial port is your device connected to? ", end="") print("\nEnter the number of the serial port your device is connected to: ", end="")
try: try:
c_port = int(input()) c_port = int(input())
if c_port < 1 or c_port > len(ports): if c_port < 1 or c_port > len(ports):
@@ -1206,6 +1206,11 @@ def main():
print("---------------------------------------------------------------------------") print("---------------------------------------------------------------------------")
print("Important! Using RNode firmware on Heltec devices should currently be") print("Important! Using RNode firmware on Heltec devices should currently be")
print("considered experimental. It is not intended for production or critical use.") print("considered experimental. It is not intended for production or critical use.")
print("")
print("Please also note that a number of users have reported issues with the serial")
print("to USB chips on Heltec LoRa V2 boards, resulting in intermittent USB comms")
print("and problems flashing and updating devices.")
print("")
print("The currently supplied firmware is provided AS-IS as a courtesey to those") print("The currently supplied firmware is provided AS-IS as a courtesey to those")
print("who would like to experiment with it. Hit enter to continue.") print("who would like to experiment with it. Hit enter to continue.")
print("---------------------------------------------------------------------------") print("---------------------------------------------------------------------------")
+3
View File
@@ -135,6 +135,9 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None):
if "peers" in ifstat and ifstat["peers"] != None: if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"])) print(" Peers : {np} reachable".format(np=ifstat["peers"]))
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None: if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">" sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr)) print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
+4 -1
View File
@@ -217,7 +217,10 @@ def prettytime(time, verbose=False):
tstr += c tstr += c
return tstr if tstr == "":
return "0s"
else:
return tstr
def phyparams(): def phyparams():
print("Required Physical Layer MTU : "+str(Reticulum.MTU)+" bytes") print("Required Physical Layer MTU : "+str(Reticulum.MTU)+" bytes")
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.0" __version__ = "0.4.4"
+106
View File
@@ -0,0 +1,106 @@
# Reticulum Development Roadmap
The development path for Reticulum is currently laid out in five distinct areas: *Comprehensibility*, *Universality*, *Functionality*, *Usability & Utility* and *Interfaceability*. Conceptualising the development of Reticulum into these areas serves to advance the implementation and work towards the Foundational Goals & Values of Reticulum.
## Comprehensibility
These efforts are aimed at improving the ease of which Reticulum is understood, and lowering the barrier to entry for people who wish to start building systems on Reticulum.
- Improving [the manual](https://markqvist.github.io/Reticulum/manual/) with tutorials specifically for beginners
- Updating the documentation to reflect recent changes and improvements
- Update descriptions of protocol mechanics
- Update announce description
- Add in-depth explanation of the IFAC system
- Software
- Update Sideband screenshots
- Update Sideband description
- Update NomadNet screenshots
- Update Sideband screenshots
- Installation
- Install docs for fedora, needs `python3-netifaces`
- Add a *Reticulum On Raspberry Pi* section
- Update *Reticulum On Android* section if necessary
- Update Android install documentation.
- Communications hardware section
- Add information about RNode external displays.
- Packet radio modems.
- Possibly add other relevant types here as well.
- Setup *Best Practices For...* / *Installation Examples* section.
- Home or office (example)
- Vehicles (example)
- No-grid/solar/remote sites (example)
## Universality
These efforts seek to broaden the universality of the Reticulum software and hardware ecosystem by continously diversifying platform support, and by improving the overall availability and ease of deployment of the Reticulum stack.
- Improved roaming support on Android
- OpenWRT support
- Create a standalone RNS Daemon app for Android
- A lightweight and portable C implementation for microcontrollers, µRNS
- A portable, high-performance Reticulum implementation in C/C++, see [#21](https://github.com/markqvist/Reticulum/discussions/21)
- Performance and memory optimisations of the Python implementation
- Bindings for other programming languages
## Functionality
These efforts aim to expand and improve the core functionality and reliability of Reticulum.
- Improve storage persist call on local client connect/disconnect
- Faster path invalidation on physical topography changes
- Better path invalidation on roaming interfaces
- Network-wide path balancing
- Distributed Destination Naming System
- Globally routable multicast
- Destination proxying: Create a new random destination, and sign it with the original destination to create verifiable ephemeral destinations. This could actually be a very powerful feature for aggregating routes in the network, and it retains destination owners control over how they are routed
- [Metric-based path selection and multiple paths](https://github.com/markqvist/Reticulum/discussions/86)
## Usability & Utility
These effors seek to make Reticulum easier to use and operate, and to expand the utility of the stack on deployed systems.
- Add bluetooth pairing code output to rnodeconf
- Easy way to share interface configurations, see [#19](https://github.com/markqvist/Reticulum/discussions/19)
- Transit traffic display in rnstatus
- JSON output mode for rnstatus
- rnid utility
- rnsign utility
- rncrypt utility
- rnsconfig utility
- Expand rnx utility to true interactive remote shell
## Interfaceability
These efforts aim to expand the types of physical and virtual interfaces that Reticulum can natively use to transport data.
- Filesystem interface
- Plain ESP32 devices (ESP-Now, WiFi, Bluetooth, etc.)
- More LoRa transceivers
- AT-compatible modems
- Direct SDR Support
- Optical mediums
- IR Transceivers
- AWDL / OWL
- HF Modems
- GNU Radio
- CAN-bus
- Raw SPI
- Raw i²c
- MQTT
- XBee
- Tor
## Active Work Areas
For each release cycle of Reticulum, improvements and additions from the five areas are selected as active work areas, and can be expected to be included in the upcoming releases within that cycle. While not entirely set in stone for each release cycle, they serve as a pointer of what to expect in the near future.
- The current `0.4.x` release cycle aims at completing:
- [x] Improve storage persist call on local client connect/disconnect
- [x] Improved roaming support on Android
- [ ] Updating the documentation to reflect recent changes and improvements
- [ ] Add bluetooth pairing code output to rnodeconf
- [ ] Improve storage persist call on every local client connect/disconnect
- [ ] Transit traffic display in rnstatus
- [ ] JSON output mode for rnstatus
- [ ] Add `rnid` utility
- [ ] Add `rnsign` utility
- [ ] Add `rncrypt` utility
- [ ] Create a standalone RNS Daemon app for Android
- Targets for related applications
- [x] Add paper offline & paper message transport to LXMF
- [x] Implement paper messaging in Nomad Network
- [x] Implement paper messaging in Sideband
- [x] Expand device support in Sideband to support older Android devices
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# Sphinx build info version 1 # Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 8cba159fb1f90a14d69c2399fad83a44 config: d8e478ae4e8ed171147d8a9f3611f9c3
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7
+5 -9
View File
@@ -160,12 +160,13 @@ Installation
Once you have obtained compatible boards, you can install the `RNode Firmware <https://github.com/markqvist/RNode_Firmware>`_ Once you have obtained compatible boards, you can install the `RNode Firmware <https://github.com/markqvist/RNode_Firmware>`_
using the `RNode Configuration Utility <https://github.com/markqvist/rnodeconfigutil>`_. using the `RNode Configuration Utility <https://github.com/markqvist/rnodeconfigutil>`_.
Make sure that ``Python3`` and ``pip`` is installed on your system, and then install If you have installed Reticulum on your system, the ``rnodeconf`` program will already be
the config utility with ``pip``: available. If not, make sure that ``Python3`` and ``pip`` is installed on your system, and
then install Reticulum with with ``pip``:
.. code:: .. code::
pip3 install rnodeconf pip3 install rns
Once installation has completed, it is time to start installing the firmware on your Once installation has completed, it is time to start installing the firmware on your
devices. Run ``rnodeconf`` in auto-install mode like so: devices. Run ``rnodeconf`` in auto-install mode like so:
@@ -176,12 +177,7 @@ devices. Run ``rnodeconf`` in auto-install mode like so:
The utility will guide you through the installation process by asking a series of The utility will guide you through the installation process by asking a series of
questions about your hardware. Simply follow the guide, and the utility will questions about your hardware. Simply follow the guide, and the utility will
auto-install and configure your devices auto-install and configure your devices.
**Important Note!** It is currently recommended to use the v1.x line of the RNode firmware,
even though the v2.x line is available for early testing. The v2.x line should still be
considered an experimental pre-release. Only use the v2.x firmware line if you want to test
out the absolutely newest version, and don't care about stability.
.. _rnode-usage: .. _rnode-usage:
+2 -2
View File
@@ -771,7 +771,7 @@ Wire Format
| | | | | | | | | | | | | | | |
00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA] 00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|| | | | | || | | | |
|| | | | +-- Hops = 0 || | | | +-- Hops = 7
|| | | +------- Packet Type = DATA || | | +------- Packet Type = DATA
|| | +--------- Destination Type = SINGLE || | +--------- Destination Type = SINGLE
|| +----------- Propagation Type = BROADCAST || +----------- Propagation Type = BROADCAST
@@ -786,7 +786,7 @@ Wire Format
| | | | | | | | | | | | | | | | | | | |
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA] 10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|| | | | | || | | | |
|| | | | +-- Hops = 0 || | | | +-- Hops = 7
|| | | +------- Packet Type = DATA || | | +------- Packet Type = DATA
|| | +--------- Destination Type = SINGLE || | +--------- Destination Type = SINGLE
|| +----------- Propagation Type = BROADCAST || +----------- Propagation Type = BROADCAST
+1 -1
View File
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.4.0 beta', VERSION: '0.4.4 beta',
LANGUAGE: 'en', LANGUAGE: 'en',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Code Examples - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Code Examples - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+3 -3
View File
@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/> <meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" /> <meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Index - Reticulum Network Stack 0.4.0 beta documentation</title> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Index - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -139,7 +139,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -165,7 +165,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Getting Started Fast - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Getting Started Fast - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+8 -11
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Supported Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Supported Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Communications Hardware - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Communications Hardware - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -349,9 +349,10 @@ boards. The following boards are supported by the auto-installer.</p>
<span id="rnode-installation"></span><h3>Installation<a class="headerlink" href="#installation" title="Permalink to this heading">#</a></h3> <span id="rnode-installation"></span><h3>Installation<a class="headerlink" href="#installation" title="Permalink to this heading">#</a></h3>
<p>Once you have obtained compatible boards, you can install the <a class="reference external" href="https://github.com/markqvist/RNode_Firmware">RNode Firmware</a> <p>Once you have obtained compatible boards, you can install the <a class="reference external" href="https://github.com/markqvist/RNode_Firmware">RNode Firmware</a>
using the <a class="reference external" href="https://github.com/markqvist/rnodeconfigutil">RNode Configuration Utility</a>. using the <a class="reference external" href="https://github.com/markqvist/rnodeconfigutil">RNode Configuration Utility</a>.
Make sure that <code class="docutils literal notranslate"><span class="pre">Python3</span></code> and <code class="docutils literal notranslate"><span class="pre">pip</span></code> is installed on your system, and then install If you have installed Reticulum on your system, the <code class="docutils literal notranslate"><span class="pre">rnodeconf</span></code> program will already be
the config utility with <code class="docutils literal notranslate"><span class="pre">pip</span></code>:</p> available. If not, make sure that <code class="docutils literal notranslate"><span class="pre">Python3</span></code> and <code class="docutils literal notranslate"><span class="pre">pip</span></code> is installed on your system, and
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">rnodeconf</span> then install Reticulum with with <code class="docutils literal notranslate"><span class="pre">pip</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">rns</span>
</pre></div> </pre></div>
</div> </div>
<p>Once installation has completed, it is time to start installing the firmware on your <p>Once installation has completed, it is time to start installing the firmware on your
@@ -361,11 +362,7 @@ devices. Run <code class="docutils literal notranslate"><span class="pre">rnodec
</div> </div>
<p>The utility will guide you through the installation process by asking a series of <p>The utility will guide you through the installation process by asking a series of
questions about your hardware. Simply follow the guide, and the utility will questions about your hardware. Simply follow the guide, and the utility will
auto-install and configure your devices</p> auto-install and configure your devices.</p>
<p><strong>Important Note!</strong> It is currently recommended to use the v1.x line of the RNode firmware,
even though the v2.x line is available for early testing. The v2.x line should still be
considered an experimental pre-release. Only use the v2.x firmware line if you want to test
out the absolutely newest version, and dont care about stability.</p>
</section> </section>
<section id="usage-with-reticulum"> <section id="usage-with-reticulum">
<span id="rnode-usage"></span><h3>Usage with Reticulum<a class="headerlink" href="#usage-with-reticulum" title="Permalink to this heading">#</a></h3> <span id="rnode-usage"></span><h3>Usage with Reticulum<a class="headerlink" href="#usage-with-reticulum" title="Permalink to this heading">#</a></h3>
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Reticulum Network Stack 0.4.0 beta documentation</title> <title>Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="#"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Supported Interfaces - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Supported Interfaces - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Supported Interfaces" href="interfaces.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Supported Interfaces" href="interfaces.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Building Networks - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Building Networks - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+4 -4
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>API Reference - Reticulum Network Stack 0.4.0 beta documentation</title> <title>API Reference - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -637,7 +637,7 @@ encrypted communication with it.</p>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.announce"> <dt class="sig sig-object py" id="RNS.Destination.announce">
<span class="sig-name descname"><span class="pre">announce</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">app_data</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">path_response</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">send</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.announce" title="Permalink to this definition">#</a></dt> <span class="sig-name descname"><span class="pre">announce</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">app_data</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">path_response</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">attached_interface</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">tag</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">send</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.announce" title="Permalink to this definition">#</a></dt>
<dd><p>Creates an announce packet for this destination and broadcasts it on all <dd><p>Creates an announce packet for this destination and broadcasts it on all
relevant interfaces. Application specific data can be added to the announce.</p> relevant interfaces. Application specific data can be added to the announce.</p>
<dl class="field-list simple"> <dl class="field-list simple">
+3 -3
View File
@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/> <meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" /> <meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Search - Reticulum Network Stack 0.4.0 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Search - Reticulum Network Stack 0.4.4 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -138,7 +138,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -164,7 +164,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search"> </a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Support Reticulum - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Support Reticulum - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+5 -5
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Understanding Reticulum - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Understanding Reticulum - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -1005,7 +1005,7 @@ proof 11
| | | | | | | | | | | | | | | |
00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA] 00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|| | | | | || | | | |
|| | | | +-- Hops = 0 || | | | +-- Hops = 7
|| | | +------- Packet Type = DATA || | | +------- Packet Type = DATA
|| | +--------- Destination Type = SINGLE || | +--------- Destination Type = SINGLE
|| +----------- Propagation Type = BROADCAST || +----------- Propagation Type = BROADCAST
@@ -1020,7 +1020,7 @@ proof 11
| | | | | | | | | | | | | | | | | | | |
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA] 10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|| | | | | || | | | |
|| | | | +-- Hops = 0 || | | | +-- Hops = 7
|| | | +------- Packet Type = DATA || | | +------- Packet Type = DATA
|| | +--------- Destination Type = SINGLE || | +--------- Destination Type = SINGLE
|| +----------- Propagation Type = BROADCAST || +----------- Propagation Type = BROADCAST
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.4.0 beta documentation</title> <title>Using Reticulum on Your System - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+3 -3
View File
@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/> <meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>What is Reticulum? - Reticulum Network Stack 0.4.0 beta documentation</title> <title>What is Reticulum? - Reticulum Network Stack 0.4.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.0 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.4.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.0 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.4.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search"> <input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
+5 -9
View File
@@ -160,12 +160,13 @@ Installation
Once you have obtained compatible boards, you can install the `RNode Firmware <https://github.com/markqvist/RNode_Firmware>`_ Once you have obtained compatible boards, you can install the `RNode Firmware <https://github.com/markqvist/RNode_Firmware>`_
using the `RNode Configuration Utility <https://github.com/markqvist/rnodeconfigutil>`_. using the `RNode Configuration Utility <https://github.com/markqvist/rnodeconfigutil>`_.
Make sure that ``Python3`` and ``pip`` is installed on your system, and then install If you have installed Reticulum on your system, the ``rnodeconf`` program will already be
the config utility with ``pip``: available. If not, make sure that ``Python3`` and ``pip`` is installed on your system, and
then install Reticulum with with ``pip``:
.. code:: .. code::
pip3 install rnodeconf pip3 install rns
Once installation has completed, it is time to start installing the firmware on your Once installation has completed, it is time to start installing the firmware on your
devices. Run ``rnodeconf`` in auto-install mode like so: devices. Run ``rnodeconf`` in auto-install mode like so:
@@ -176,12 +177,7 @@ devices. Run ``rnodeconf`` in auto-install mode like so:
The utility will guide you through the installation process by asking a series of The utility will guide you through the installation process by asking a series of
questions about your hardware. Simply follow the guide, and the utility will questions about your hardware. Simply follow the guide, and the utility will
auto-install and configure your devices auto-install and configure your devices.
**Important Note!** It is currently recommended to use the v1.x line of the RNode firmware,
even though the v2.x line is available for early testing. The v2.x line should still be
considered an experimental pre-release. Only use the v2.x firmware line if you want to test
out the absolutely newest version, and don't care about stability.
.. _rnode-usage: .. _rnode-usage:
+2 -2
View File
@@ -771,7 +771,7 @@ Wire Format
| | | | | | | | | | | | | | | |
00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA] 00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|| | | | | || | | | |
|| | | | +-- Hops = 0 || | | | +-- Hops = 7
|| | | +------- Packet Type = DATA || | | +------- Packet Type = DATA
|| | +--------- Destination Type = SINGLE || | +--------- Destination Type = SINGLE
|| +----------- Propagation Type = BROADCAST || +----------- Propagation Type = BROADCAST
@@ -786,7 +786,7 @@ Wire Format
| | | | | | | | | | | | | | | | | | | |
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA] 10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|| | | | | || | | | |
|| | | | +-- Hops = 0 || | | | +-- Hops = 7
|| | | +------- Packet Type = DATA || | | +------- Packet Type = DATA
|| | +--------- Destination Type = SINGLE || | +--------- Destination Type = SINGLE
|| +----------- Propagation Type = BROADCAST || +----------- Propagation Type = BROADCAST