mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-06-23 04:16:12 -07:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 700601d63e | |||
| 274c7199b0 | |||
| 7960226883 | |||
| bb74878e94 | |||
| 549d22be68 | |||
| 5c2c935b6f | |||
| 8402541c73 | |||
| c34c268a6a | |||
| 8fcdc4613c | |||
| f645fa569b | |||
| 469947dab9 | |||
| 2386fc3635 | |||
| e9e98a00c2 | |||
| b305eb8e0a | |||
| dd7931d421 | |||
| 191dce1301 | |||
| 3b5a27ba60 | |||
| 3c91f7f18b | |||
| 171457713b | |||
| 67ee8d6aab | |||
| 13fa7d49d9 | |||
| 66d921e669 | |||
| 85f60ea04e | |||
| 4870e741f6 | |||
| f71c1986af | |||
| 30d8e351dd | |||
| 5e62e3bc22 | |||
| 1a67e276ad | |||
| df37a4a884 | |||
| d26bbbd59f | |||
| 2a264fa7d6 | |||
| d5e0a461cf | |||
| e28dbd4afa | |||
| 8626dcd69f | |||
| e34f21f4dc | |||
| f692e81b8e | |||
| 28e43b52f9 | |||
| 680d17fb98 | |||
| 1e477c976c | |||
| ab301cdb79 | |||
| cecb4b3acb | |||
| de53a105a4 | |||
| 9e4ae3c6fe | |||
| 3482d84bc0 | |||
| 51c5c85fcd | |||
| 57aeab43a2 | |||
| 92cccddaab | |||
| 3de182192a | |||
| aca6b0c110 | |||
| 3d6e7a9597 | |||
| 21da55dd39 | |||
| 9e664af1c6 | |||
| 7736ed589e | |||
| f22504d080 | |||
| f22e5cc200 | |||
| 87b73b6c67 | |||
| 36906f6567 | |||
| 52edb54d21 | |||
| 88b88b9b64 | |||
| 76fcad0b53 | |||
| 01e520b082 | |||
| 1d2a0fe4c8 | |||
| 0f19ced9d3 | |||
| 4ca32c039d | |||
| 81ec701240 | |||
| b16d614495 | |||
| 5f7e37187f |
@@ -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)
|
||||
|
||||
For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects/reticulum/)
|
||||
For more info, see [reticulum.network](https://reticulum.network/)
|
||||
|
||||
## Notable Features
|
||||
- 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
|
||||
- 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
|
||||
If you want to quickly get an idea of what Reticulum can do, take a look at the
|
||||
following resources.
|
||||
@@ -177,39 +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
|
||||
considered relatively stable at the moment, but could change if warranted.
|
||||
|
||||
## Development Roadmap
|
||||
- Version 0.4.0
|
||||
- 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
|
||||
- Planned, but not yet scheduled
|
||||
- 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
|
||||
The installation of the default `rns` package requires the dependencies listed
|
||||
below. Almost all systems and distributions have readily available packages for
|
||||
@@ -273,7 +245,7 @@ file:
|
||||
[[RNS Testnet I2P Hub A]]
|
||||
type = I2PInterface
|
||||
enabled = yes
|
||||
peers = mrwqlsioq4hoo2lmeeud7dkfscnm7yxak7dmiyvsrnpfag3z5tsq.b32.i2p
|
||||
peers = pmlm3l5rpympihoy2o5ago43kluei2jjjzsalcuiuylbve3mwi2a.b32.i2p
|
||||
|
||||
# Interface to I2P Hub B
|
||||
[[RNS Testnet I2P Hub B]]
|
||||
|
||||
@@ -33,7 +33,7 @@ def hkdf(length=None, derive_from=None, salt=None, context=None):
|
||||
if length == None or length < 1:
|
||||
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")
|
||||
|
||||
if salt == None or len(salt) == 0:
|
||||
@@ -54,4 +54,4 @@ def hkdf(length=None, derive_from=None, salt=None, context=None):
|
||||
block = hmac_sha256(pseudorandom_key, block + context + bytes([i + 1]))
|
||||
derived += block
|
||||
|
||||
return derived[:length]
|
||||
return derived[:length]
|
||||
|
||||
+48
-20
@@ -69,6 +69,8 @@ class Destination:
|
||||
OUT = 0x12;
|
||||
directions = [IN, OUT]
|
||||
|
||||
PR_TAG_WINDOW = 30
|
||||
|
||||
@staticmethod
|
||||
def expand_name(identity, app_name, *aspects):
|
||||
"""
|
||||
@@ -132,6 +134,7 @@ class Destination:
|
||||
self.proof_strategy = Destination.PROVE_NONE
|
||||
self.mtu = 0
|
||||
|
||||
self.path_responses = {}
|
||||
self.links = []
|
||||
|
||||
if identity == None and direction == Destination.IN and self.type != Destination.PLAIN:
|
||||
@@ -163,7 +166,7 @@ class Destination:
|
||||
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
|
||||
relevant interfaces. Application specific data can be added to the announce.
|
||||
@@ -173,35 +176,60 @@ class Destination:
|
||||
"""
|
||||
if self.type != Destination.SINGLE:
|
||||
raise TypeError("Only SINGLE destination types can be announced")
|
||||
|
||||
destination_hash = self.hash
|
||||
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
|
||||
|
||||
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
|
||||
|
||||
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash
|
||||
if app_data != None:
|
||||
signed_data += app_data
|
||||
now = time.time()
|
||||
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)
|
||||
|
||||
signature = self.identity.sign(signed_data)
|
||||
for entry_tag in stale_responses:
|
||||
self.path_responses.pop(entry_tag)
|
||||
|
||||
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+signature
|
||||
if (path_response == True and tag != None) and tag in self.path_responses:
|
||||
# This code is currently not used, since Transport will block duplicate
|
||||
# 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]
|
||||
|
||||
else:
|
||||
destination_hash = self.hash
|
||||
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
|
||||
|
||||
if app_data != None:
|
||||
announce_data += app_data
|
||||
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
|
||||
|
||||
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash
|
||||
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:
|
||||
announce_context = RNS.Packet.PATH_RESPONSE
|
||||
else:
|
||||
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:
|
||||
announce_packet.send()
|
||||
|
||||
+15
-3
@@ -255,10 +255,22 @@ class Identity:
|
||||
RNS.Identity.remember(packet.get_hash(), destination_hash, public_key, app_data)
|
||||
del announced_identity
|
||||
|
||||
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), RNS.LOG_EXTREME)
|
||||
if packet.rssi != None or packet.snr != None:
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
@@ -193,7 +193,12 @@ class AndroidBluetoothManager():
|
||||
if self.connected and self.rfcomm_reader != None:
|
||||
available = self.rfcomm_reader.available()
|
||||
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:
|
||||
return bytes([])
|
||||
else:
|
||||
@@ -249,13 +254,14 @@ class RNodeInterface(Interface):
|
||||
|
||||
# Driver overrides for speficic chips
|
||||
from usbserial4a import serial4a as pyserial
|
||||
proxy = pyserial.get_serial_port
|
||||
if vid == 0x1A86 and pid == 0x55D4:
|
||||
# Force CDC driver for Qinheng CH34x
|
||||
RNS.log("Using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
|
||||
from usbserial4a.cdcacmserial4a import CdcAcmSerial
|
||||
proxy = CdcAcmSerial
|
||||
|
||||
serial = pyserial.get_serial_port(
|
||||
serial = proxy(
|
||||
port,
|
||||
baudrate = 115200,
|
||||
bytesize = 8,
|
||||
@@ -276,7 +282,7 @@ class RNodeInterface(Interface):
|
||||
serial.timeout = 0.1
|
||||
elif vid == 0x10C4:
|
||||
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
||||
serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||
serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||
serial.USB_READ_TIMEOUT_MILLIS = 12
|
||||
serial.timeout = 0.012
|
||||
elif vid == 0x1A86 and pid == 0x55D4:
|
||||
@@ -323,11 +329,13 @@ class RNodeInterface(Interface):
|
||||
from usbserial4a import serial4a as serial
|
||||
self.parity = "N"
|
||||
|
||||
self.bt_target_device_name = target_device_name
|
||||
self.bt_target_device_address = target_device_address
|
||||
if allow_bluetooth:
|
||||
self.bt_manager = AndroidBluetoothManager(
|
||||
owner = self,
|
||||
target_device_name = target_device_name,
|
||||
target_device_address = target_device_address
|
||||
target_device_name = self.bt_target_device_name,
|
||||
target_device_address = self.bt_target_device_address
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
self.bt_manager.connect_any_device()
|
||||
|
||||
@@ -549,7 +564,7 @@ class RNodeInterface(Interface):
|
||||
thread.start()
|
||||
|
||||
self.detect()
|
||||
sleep(0.4)
|
||||
sleep(0.5)
|
||||
|
||||
if not self.detected:
|
||||
raise IOError("Could not detect device")
|
||||
@@ -587,22 +602,22 @@ class RNodeInterface(Interface):
|
||||
|
||||
def initRadio(self):
|
||||
self.setFrequency()
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.15)
|
||||
|
||||
self.setBandwidth()
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.15)
|
||||
|
||||
self.setTXPower()
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.15)
|
||||
|
||||
self.setSpreadingFactor()
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.15)
|
||||
|
||||
self.setCodingRate()
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.15)
|
||||
|
||||
self.setRadioState(KISS.RADIO_STATE_ON)
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.15)
|
||||
|
||||
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])
|
||||
@@ -1057,10 +1072,10 @@ class RNodeInterface(Interface):
|
||||
try:
|
||||
time.sleep(self.reconnect_w)
|
||||
if self.serial != None and self.port != None:
|
||||
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_EXTREME)
|
||||
|
||||
if self.bt_manager != None:
|
||||
RNS.log("Attempting to reconnect Bluetooth device for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
RNS.log("Attempting to reconnect Bluetooth device for "+str(self)+"...", RNS.LOG_EXTREME)
|
||||
|
||||
self.open_port()
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ class AutoInterface(Interface):
|
||||
|
||||
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):
|
||||
import importlib
|
||||
if importlib.util.find_spec('netifaces') != None:
|
||||
@@ -70,8 +75,10 @@ class AutoInterface(Interface):
|
||||
self.peers = {}
|
||||
self.link_local_addresses = []
|
||||
self.adopted_interfaces = {}
|
||||
self.interface_servers = {}
|
||||
self.multicast_echoes = {}
|
||||
self.timed_out_interfaces = {}
|
||||
self.carrier_changed = False
|
||||
|
||||
self.outbound_udp_socket = None
|
||||
|
||||
@@ -199,11 +206,6 @@ class AutoInterface(Interface):
|
||||
peering_wait = self.announce_interval*1.2
|
||||
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
|
||||
socketserver.UDPServer.address_family = socket.AF_INET6
|
||||
|
||||
@@ -212,9 +214,10 @@ class AutoInterface(Interface):
|
||||
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
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.start()
|
||||
|
||||
@@ -277,7 +280,33 @@ class AutoInterface(Interface):
|
||||
if address["addr"].startswith("fe80:"):
|
||||
link_local_addr = address["addr"].split("%")[0]
|
||||
if link_local_addr != self.adopted_interfaces[ifname]:
|
||||
old_link_local_address = self.adopted_interfaces[ifname]
|
||||
RNS.log("Replacing link-local address "+str(old_link_local_address)+" for "+str(ifname)+" with "+str(link_local_addr), RNS.LOG_DEBUG)
|
||||
self.adopted_interfaces[ifname] = link_local_addr
|
||||
self.link_local_addresses.append(link_local_addr)
|
||||
|
||||
if old_link_local_address in self.link_local_addresses:
|
||||
self.link_local_addresses.remove(old_link_local_address)
|
||||
|
||||
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:
|
||||
RNS.log("Shutting down previous UDP listener for "+str(self)+" "+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()
|
||||
|
||||
RNS.log("Starting new UDP listener for "+str(self)+" "+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:
|
||||
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
@@ -289,10 +318,12 @@ class AutoInterface(Interface):
|
||||
|
||||
if now - last_multicast_echo > self.multicast_echo_timeout:
|
||||
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False:
|
||||
self.carrier_changed = True
|
||||
RNS.log("Multicast echo timeout for "+str(ifname)+". Carrier lost.", RNS.LOG_WARNING)
|
||||
self.timed_out_interfaces[ifname] = True
|
||||
else:
|
||||
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == True:
|
||||
self.carrier_changed = True
|
||||
RNS.log(str(self)+" Carrier recovered on "+str(ifname), RNS.LOG_WARNING)
|
||||
self.timed_out_interfaces[ifname] = False
|
||||
|
||||
|
||||
+125
-27
@@ -161,8 +161,6 @@ class I2PController:
|
||||
raise tn.status["exception"]
|
||||
|
||||
else:
|
||||
self.client_tunnels[i2p_destination] = True
|
||||
owner.awaiting_i2p_tunnel = False
|
||||
if owner.socket != None:
|
||||
if hasattr(owner.socket, "close"):
|
||||
if callable(owner.socket.close):
|
||||
@@ -175,6 +173,8 @@ class I2PController:
|
||||
owner.socket.close()
|
||||
except Exception as 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)
|
||||
|
||||
@@ -383,6 +383,11 @@ class I2PInterfacePeer(Interface):
|
||||
I2P_PROBE_AFTER = 10
|
||||
I2P_PROBE_INTERVAL = 9
|
||||
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):
|
||||
self.rxb = 0
|
||||
@@ -409,6 +414,30 @@ class I2PInterfacePeer(Interface):
|
||||
self.i2p_tunnel_ready = False
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
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_grace = None
|
||||
@@ -454,7 +483,7 @@ class I2PInterfacePeer(Interface):
|
||||
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)
|
||||
|
||||
time.sleep(15)
|
||||
time.sleep(8)
|
||||
|
||||
thread = threading.Thread(target=tunnel_job)
|
||||
thread.daemon = True
|
||||
@@ -463,6 +492,7 @@ class I2PInterfacePeer(Interface):
|
||||
def wait_job():
|
||||
while self.awaiting_i2p_tunnel:
|
||||
time.sleep(0.25)
|
||||
time.sleep(2)
|
||||
|
||||
if not self.kiss_framing:
|
||||
self.wants_tunnel = True
|
||||
@@ -482,18 +512,11 @@ class I2PInterfacePeer(Interface):
|
||||
|
||||
|
||||
def set_timeouts_linux(self):
|
||||
if not self.i2p_tunneled:
|
||||
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.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(I2PInterfacePeer.TCP_PROBE_AFTER))
|
||||
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.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))
|
||||
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):
|
||||
if hasattr(socket, "TCP_KEEPALIVE"):
|
||||
@@ -502,22 +525,19 @@ class I2PInterfacePeer(Interface):
|
||||
TCP_KEEPIDLE = 0x10
|
||||
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
|
||||
if not self.i2p_tunneled:
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.TCP_PROBE_AFTER))
|
||||
else:
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
|
||||
|
||||
def shutdown_socket(self, socket):
|
||||
if callable(socket.close):
|
||||
|
||||
def shutdown_socket(self, target_socket):
|
||||
if callable(target_socket.close):
|
||||
try:
|
||||
socket.shutdown(socket.SHUT_RDWR)
|
||||
if socket != None:
|
||||
target_socket.shutdown(socket.SHUT_RDWR)
|
||||
except Exception as e:
|
||||
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
|
||||
|
||||
try:
|
||||
socket.close()
|
||||
if socket != None:
|
||||
target_socket.close()
|
||||
except Exception as e:
|
||||
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
|
||||
|
||||
@@ -571,7 +591,6 @@ class I2PInterfacePeer(Interface):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def reconnect(self):
|
||||
if self.initiator:
|
||||
if not self.reconnecting:
|
||||
@@ -632,6 +651,7 @@ class I2PInterfacePeer(Interface):
|
||||
self.socket.sendall(data)
|
||||
self.writing = False
|
||||
self.txb += len(data)
|
||||
self.last_write = time.time()
|
||||
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
||||
self.parent_interface.txb += len(data)
|
||||
@@ -642,8 +662,59 @@ class I2PInterfacePeer(Interface):
|
||||
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):
|
||||
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
|
||||
escape = False
|
||||
data_buffer = b""
|
||||
@@ -653,6 +724,7 @@ class I2PInterfacePeer(Interface):
|
||||
data_in = self.socket.recv(4096)
|
||||
if len(data_in) > 0:
|
||||
pointer = 0
|
||||
self.last_read = time.time()
|
||||
while pointer < len(data_in):
|
||||
byte = data_in[pointer]
|
||||
pointer += 1
|
||||
@@ -705,6 +777,11 @@ class I2PInterfacePeer(Interface):
|
||||
data_buffer = data_buffer+bytes([byte])
|
||||
else:
|
||||
self.online = False
|
||||
|
||||
self.wd_reset = True
|
||||
time.sleep(2)
|
||||
self.wd_reset = False
|
||||
|
||||
if self.initiator and not self.detached:
|
||||
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
|
||||
self.reconnect()
|
||||
@@ -754,7 +831,7 @@ class I2PInterfacePeer(Interface):
|
||||
class I2PInterface(Interface):
|
||||
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.txb = 0
|
||||
|
||||
@@ -780,6 +857,9 @@ class I2PInterface(Interface):
|
||||
self.bind_port = self.i2p.get_free_port()
|
||||
self.address = (self.bind_ip, self.bind_port)
|
||||
self.bitrate = I2PInterface.BITRATE_GUESS
|
||||
self.ifac_size = ifac_size
|
||||
self.ifac_netname = ifac_netname
|
||||
self.ifac_netkey = ifac_netkey
|
||||
|
||||
self.online = False
|
||||
|
||||
@@ -850,9 +930,27 @@ class I2PInterface(Interface):
|
||||
spawned_interface.parent_interface = self
|
||||
spawned_interface.online = True
|
||||
spawned_interface.bitrate = self.bitrate
|
||||
|
||||
spawned_interface.ifac_size = self.ifac_size
|
||||
spawned_interface.ifac_netname = self.ifac_netname
|
||||
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_grace = self.announce_rate_grace
|
||||
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
|
||||
|
||||
@@ -483,9 +483,27 @@ class TCPServerInterface(Interface):
|
||||
spawned_interface.target_port = str(handler.client_address[1])
|
||||
spawned_interface.parent_interface = self
|
||||
spawned_interface.bitrate = self.bitrate
|
||||
|
||||
spawned_interface.ifac_size = self.ifac_size
|
||||
spawned_interface.ifac_netname = self.ifac_netname
|
||||
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_grace = self.announce_rate_grace
|
||||
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
|
||||
|
||||
+9
-2
@@ -272,6 +272,8 @@ class Link:
|
||||
self.rtt = time.time() - self.request_time
|
||||
self.attached_interface = packet.receiving_interface
|
||||
self.__remote_identity = self.destination.identity
|
||||
self.status = Link.ACTIVE
|
||||
self.activated_at = time.time()
|
||||
RNS.Transport.activate_link(self)
|
||||
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE)
|
||||
rtt_data = umsgpack.packb(self.rtt)
|
||||
@@ -279,8 +281,6 @@ class Link:
|
||||
rtt_packet.send()
|
||||
self.had_outbound()
|
||||
|
||||
self.status = Link.ACTIVE
|
||||
self.activated_at = time.time()
|
||||
if self.callbacks.link_established != None:
|
||||
thread = threading.Thread(target=self.callbacks.link_established, args=(self,))
|
||||
thread.daemon = True
|
||||
@@ -900,6 +900,13 @@ class Link:
|
||||
def register_incoming_resource(self, 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):
|
||||
if resource in self.outgoing_resources:
|
||||
self.outgoing_resources.remove(resource)
|
||||
|
||||
+19
-13
@@ -172,20 +172,26 @@ class Resource:
|
||||
|
||||
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)
|
||||
if resource.link.callbacks.resource_started != None:
|
||||
try:
|
||||
resource.link.callbacks.resource_started(resource)
|
||||
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("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
|
||||
if resource.link.callbacks.resource_started != None:
|
||||
try:
|
||||
resource.link.callbacks.resource_started(resource)
|
||||
except Exception as e:
|
||||
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:
|
||||
RNS.log("Could not decode resource advertisement, dropping resource", RNS.LOG_DEBUG)
|
||||
return None
|
||||
@@ -397,8 +403,7 @@ class Resource:
|
||||
thread.start()
|
||||
|
||||
def __advertise_job(self):
|
||||
data = ResourceAdvertisement(self).pack()
|
||||
self.advertisement_packet = RNS.Packet(self.link, data, context=RNS.Packet.RESOURCE_ADV)
|
||||
self.advertisement_packet = RNS.Packet(self.link, ResourceAdvertisement(self).pack(), context=RNS.Packet.RESOURCE_ADV)
|
||||
while not self.link.ready_for_new_resource():
|
||||
self.status = Resource.QUEUED
|
||||
sleep(0.25)
|
||||
@@ -445,7 +450,8 @@ class Resource:
|
||||
try:
|
||||
RNS.log("No part requests received, retrying resource advertisement...", RNS.LOG_DEBUG)
|
||||
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.adv_sent = self.last_activity
|
||||
sleep_time = 0.001
|
||||
|
||||
+19
-4
@@ -631,12 +631,18 @@ class Reticulum:
|
||||
i2p_peers = c.as_list("peers") if "peers" in c else None
|
||||
connectable = c.as_bool("connectable") if "connectable" in c else False
|
||||
|
||||
if ifac_size == None:
|
||||
ifac_size = 16
|
||||
|
||||
interface = I2PInterface.I2PInterface(
|
||||
RNS.Transport,
|
||||
name,
|
||||
Reticulum.storagepath,
|
||||
i2p_peers,
|
||||
connectable = connectable,
|
||||
ifac_size = ifac_size,
|
||||
ifac_netname = ifac_netname,
|
||||
ifac_netkey = ifac_netkey,
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
@@ -653,10 +659,6 @@ class Reticulum:
|
||||
interface.announce_cap = announce_cap
|
||||
if configured_bitrate:
|
||||
interface.bitrate = configured_bitrate
|
||||
if ifac_size != None:
|
||||
interface.ifac_size = ifac_size
|
||||
else:
|
||||
interface.ifac_size = 16
|
||||
|
||||
if c["type"] == "SerialInterface":
|
||||
port = c["port"] if "port" in c else None
|
||||
@@ -1071,6 +1073,19 @@ class Reticulum:
|
||||
else:
|
||||
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 interface.bitrate != None:
|
||||
ifstats["bitrate"] = interface.bitrate
|
||||
|
||||
+156
-67
@@ -66,6 +66,7 @@ class Transport:
|
||||
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_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
|
||||
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
|
||||
tunnels = {} # A table storing tunnels to other transport instances
|
||||
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_pr_tags = [] # A table for keeping track of tagged path requests
|
||||
@@ -116,6 +118,8 @@ class Transport:
|
||||
jobs_locked = False
|
||||
jobs_running = False
|
||||
job_interval = 0.250
|
||||
links_last_checked = 0.0
|
||||
links_check_interval = 1.0
|
||||
receipts_last_checked = 0.0
|
||||
receipts_check_interval = 1.0
|
||||
announces_last_checked = 0.0
|
||||
@@ -144,13 +148,14 @@ class Transport:
|
||||
RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE)
|
||||
|
||||
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
|
||||
if os.path.isfile(packet_hashlist_path):
|
||||
try:
|
||||
file = open(packet_hashlist_path, "rb")
|
||||
Transport.packet_hashlist = umsgpack.unpackb(file.read())
|
||||
file.close()
|
||||
except Exception as e:
|
||||
RNS.log("Could not load packet hashlist from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
if os.path.isfile(packet_hashlist_path):
|
||||
try:
|
||||
file = open(packet_hashlist_path, "rb")
|
||||
Transport.packet_hashlist = umsgpack.unpackb(file.read())
|
||||
file.close()
|
||||
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
|
||||
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||
@@ -164,8 +169,7 @@ class Transport:
|
||||
Transport.control_hashes.append(Transport.tunnel_synthesize_destination.hash)
|
||||
|
||||
Transport.jobs_running = False
|
||||
thread = threading.Thread(target=Transport.jobloop)
|
||||
thread.daemon = True
|
||||
thread = threading.Thread(target=Transport.jobloop, daemon=True)
|
||||
thread.start()
|
||||
|
||||
if RNS.Reticulum.transport_enabled():
|
||||
@@ -265,8 +269,6 @@ class Transport:
|
||||
except Exception as e:
|
||||
RNS.log("Could not load tunnel table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
|
||||
|
||||
RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE)
|
||||
|
||||
# Synthesize tunnels for any interfaces wanting it
|
||||
@@ -284,10 +286,44 @@ class Transport:
|
||||
@staticmethod
|
||||
def jobs():
|
||||
outgoing = []
|
||||
path_requests = []
|
||||
Transport.jobs_running = True
|
||||
|
||||
try:
|
||||
if not Transport.jobs_locked:
|
||||
|
||||
# Process active and pending link lists
|
||||
if time.time() > Transport.links_last_checked+Transport.links_check_interval:
|
||||
|
||||
for link in Transport.pending_links:
|
||||
if link.status == RNS.Link.CLOSED:
|
||||
# If we are not a Transport Instance, finding a pending link
|
||||
# that was never activated will trigger an expiry of the path
|
||||
# to the destination, and an attempt to rediscover the path.
|
||||
if not RNS.Reticulum.transport_enabled():
|
||||
Transport.expire_path(link.destination.hash)
|
||||
|
||||
# If we are connected to a shared instance, it will take
|
||||
# care of sending out a new path request. If not, we will
|
||||
# send one directly.
|
||||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
last_path_request = 0
|
||||
if link.destination.hash in Transport.path_requests:
|
||||
last_path_request = Transport.path_requests[link.destination.hash]
|
||||
|
||||
if time.time() - last_path_request > Transport.PATH_REQUEST_MI:
|
||||
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link.destination.hash)+" since an attempted link was never established", RNS.LOG_DEBUG)
|
||||
if not link.destination.hash in path_requests:
|
||||
path_requests.append(link.destination.hash)
|
||||
|
||||
Transport.pending_links.remove(link)
|
||||
|
||||
for link in Transport.active_links:
|
||||
if link.status == RNS.Link.CLOSED:
|
||||
Transport.active_links.remove(link)
|
||||
|
||||
Transport.links_last_checked = time.time()
|
||||
|
||||
# Process receipts list for timed-out packets
|
||||
if time.time() > Transport.receipts_last_checked+Transport.receipts_check_interval:
|
||||
while len(Transport.receipts) > Transport.MAX_RECEIPTS:
|
||||
@@ -379,8 +415,32 @@ class Transport:
|
||||
stale_links = []
|
||||
for link_id in Transport.link_table:
|
||||
link_entry = Transport.link_table[link_id]
|
||||
if time.time() > link_entry[0] + Transport.LINK_TIMEOUT:
|
||||
stale_links.append(link_id)
|
||||
|
||||
if link_entry[7] == True:
|
||||
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 a local client
|
||||
# attempt to rediscover a path to the destination, if this
|
||||
# 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 local client link was never established", RNS.LOG_DEBUG)
|
||||
if not link_entry[6] in path_requests:
|
||||
path_requests.append(link_entry[6])
|
||||
|
||||
if not RNS.Reticulum.transport_enabled():
|
||||
# Drop current path if we are not a transport instance, to
|
||||
# allow using higher-hop count paths or reused announces
|
||||
# from newly adjacent transport instances.
|
||||
Transport.expire_path(link_entry[6])
|
||||
|
||||
# Cull the path table
|
||||
stale_paths = []
|
||||
@@ -503,6 +563,10 @@ class Transport:
|
||||
|
||||
Transport.tables_last_culled = time.time()
|
||||
|
||||
else:
|
||||
# Transport jobs were locked, do nothing
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An exception occurred while running Transport jobs.", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
@@ -512,6 +576,9 @@ class Transport:
|
||||
for packet in outgoing:
|
||||
packet.send()
|
||||
|
||||
for destination_hash in path_requests:
|
||||
Transport.request_path(destination_hash)
|
||||
|
||||
@staticmethod
|
||||
def transmit(interface, raw):
|
||||
try:
|
||||
@@ -539,9 +606,6 @@ class Transport:
|
||||
|
||||
Transport.jobs_locked = True
|
||||
|
||||
# TODO: This updateHash call might be redundant
|
||||
# packet.update_hash()
|
||||
|
||||
sent = False
|
||||
outbound_time = time.time()
|
||||
|
||||
@@ -833,7 +897,7 @@ class Transport:
|
||||
def inbound(raw, interface=None):
|
||||
# If interface access codes are enabled,
|
||||
# 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:
|
||||
# Check that IFAC flag is set
|
||||
if raw[0] & 0x80 == 0x80:
|
||||
@@ -871,6 +935,9 @@ class Transport:
|
||||
# If the flag is set, drop the packet
|
||||
return
|
||||
|
||||
else:
|
||||
return
|
||||
|
||||
while (Transport.jobs_running):
|
||||
sleep(0.0005)
|
||||
|
||||
@@ -994,15 +1061,19 @@ class Transport:
|
||||
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
||||
|
||||
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
|
||||
link_entry = [ time.time(), # 0: Timestamp,
|
||||
link_entry = [ now, # 0: Timestamp,
|
||||
next_hop, # 1: Next-hop transport ID
|
||||
outbound_interface, # 2: Next-hop interface
|
||||
remaining_hops, # 3: Remaining hops
|
||||
packet.receiving_interface, # 4: Received on interface
|
||||
packet.hops, # 5: Taken hops
|
||||
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
|
||||
|
||||
@@ -1202,9 +1273,9 @@ class Transport:
|
||||
|
||||
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
|
||||
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
|
||||
else:
|
||||
expires = now + Transport.PATHFINDER_E
|
||||
@@ -1377,10 +1448,11 @@ class Transport:
|
||||
|
||||
# Handling for linkrequests to local destinations
|
||||
elif packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||
for destination in Transport.destinations:
|
||||
if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
|
||||
packet.destination = destination
|
||||
destination.receive(packet)
|
||||
if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
|
||||
for destination in Transport.destinations:
|
||||
if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
|
||||
packet.destination = destination
|
||||
destination.receive(packet)
|
||||
|
||||
# Handling for local data packets
|
||||
elif packet.packet_type == RNS.Packet.DATA:
|
||||
@@ -1414,14 +1486,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:
|
||||
link_entry = Transport.link_table[packet.destination_hash]
|
||||
if packet.receiving_interface == link_entry[2]:
|
||||
# TODO: Should we validate the LR proof at each transport
|
||||
# step before transporting it?
|
||||
# RNS.log("Link request proof received on correct interface, transporting it via "+str(link_entry[4]), RNS.LOG_EXTREME)
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += 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)
|
||||
try:
|
||||
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
|
||||
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
|
||||
peer_identity = RNS.Identity.recall(link_entry[6])
|
||||
peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
|
||||
|
||||
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
|
||||
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:
|
||||
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||
else:
|
||||
@@ -1596,7 +1683,7 @@ class Transport:
|
||||
|
||||
@staticmethod
|
||||
def register_link(link):
|
||||
RNS.log("Registering link "+str(link), RNS.LOG_DEBUG)
|
||||
RNS.log("Registering link "+str(link), RNS.LOG_EXTREME)
|
||||
if link.initiator:
|
||||
Transport.pending_links.append(link)
|
||||
else:
|
||||
@@ -1604,7 +1691,7 @@ class Transport:
|
||||
|
||||
@staticmethod
|
||||
def activate_link(link):
|
||||
RNS.log("Activating link "+str(link), RNS.LOG_DEBUG)
|
||||
RNS.log("Activating link "+str(link), RNS.LOG_EXTREME)
|
||||
if link in Transport.pending_links:
|
||||
Transport.pending_links.remove(link)
|
||||
Transport.active_links.append(link)
|
||||
@@ -1825,6 +1912,7 @@ class Transport:
|
||||
on_interface.announce_allowed_at = now + wait_time
|
||||
|
||||
packet.send()
|
||||
Transport.path_requests[destination_hash] = time.time()
|
||||
|
||||
@staticmethod
|
||||
def path_request_handler(data, packet):
|
||||
@@ -1901,10 +1989,9 @@ class Transport:
|
||||
|
||||
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), 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)
|
||||
|
||||
|
||||
elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table):
|
||||
packet = Transport.destination_table[destination_hash][6]
|
||||
next_hop = Transport.destination_table[destination_hash][1]
|
||||
@@ -1949,9 +2036,10 @@ class Transport:
|
||||
# Forward path request on all interfaces
|
||||
# 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)
|
||||
request_tag = RNS.Identity.get_random_hash()
|
||||
for interface in Transport.interfaces:
|
||||
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:
|
||||
if destination_hash in Transport.discovery_path_requests:
|
||||
@@ -2078,41 +2166,42 @@ class Transport:
|
||||
|
||||
@staticmethod
|
||||
def save_packet_hashlist():
|
||||
if hasattr(Transport, "saving_packet_hashlist"):
|
||||
wait_interval = 0.2
|
||||
wait_timeout = 5
|
||||
wait_start = time.time()
|
||||
while Transport.saving_packet_hashlist:
|
||||
time.sleep(wait_interval)
|
||||
if time.time() > wait_start+wait_timeout:
|
||||
RNS.log("Could not save packet hashlist to storage, waiting for previous save operation timed out.", RNS.LOG_ERROR)
|
||||
return False
|
||||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
if hasattr(Transport, "saving_packet_hashlist"):
|
||||
wait_interval = 0.2
|
||||
wait_timeout = 5
|
||||
wait_start = time.time()
|
||||
while Transport.saving_packet_hashlist:
|
||||
time.sleep(wait_interval)
|
||||
if time.time() > wait_start+wait_timeout:
|
||||
RNS.log("Could not save packet hashlist to storage, waiting for previous save operation timed out.", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
try:
|
||||
Transport.saving_packet_hashlist = True
|
||||
save_start = time.time()
|
||||
try:
|
||||
Transport.saving_packet_hashlist = True
|
||||
save_start = time.time()
|
||||
|
||||
if not RNS.Reticulum.transport_enabled():
|
||||
Transport.packet_hashlist = []
|
||||
else:
|
||||
RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG)
|
||||
if not RNS.Reticulum.transport_enabled():
|
||||
Transport.packet_hashlist = []
|
||||
else:
|
||||
RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG)
|
||||
|
||||
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
|
||||
file = open(packet_hashlist_path, "wb")
|
||||
file.write(umsgpack.packb(Transport.packet_hashlist))
|
||||
file.close()
|
||||
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
|
||||
file = open(packet_hashlist_path, "wb")
|
||||
file.write(umsgpack.packb(Transport.packet_hashlist))
|
||||
file.close()
|
||||
|
||||
save_time = time.time() - save_start
|
||||
if save_time < 1:
|
||||
time_str = str(round(save_time*1000,2))+"ms"
|
||||
else:
|
||||
time_str = str(round(save_time,2))+"s"
|
||||
RNS.log("Saved packet hashlist in "+time_str, RNS.LOG_DEBUG)
|
||||
save_time = time.time() - save_start
|
||||
if save_time < 1:
|
||||
time_str = str(round(save_time*1000,2))+"ms"
|
||||
else:
|
||||
time_str = str(round(save_time,2))+"s"
|
||||
RNS.log("Saved packet hashlist in "+time_str, RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
except Exception as e:
|
||||
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
|
||||
|
||||
@@ -218,7 +218,7 @@ UPD_DIR = None
|
||||
FWD_DIR = None
|
||||
|
||||
try:
|
||||
CNF_DIR = os.path.expanduser("~/.local/rnodeconf")
|
||||
CNF_DIR = os.path.expanduser("~/.config/rnodeconf")
|
||||
UPD_DIR = CNF_DIR+"/update"
|
||||
FWD_DIR = CNF_DIR+"/firmware"
|
||||
|
||||
@@ -906,7 +906,7 @@ def ensure_firmware_file(fw_filename):
|
||||
pass
|
||||
else:
|
||||
RNS.log("")
|
||||
RNS.log("Firmware corrupt.")
|
||||
RNS.log("Firmware corrupt. Try clearing the local firmware cache with: rnodeconf --clear-cache")
|
||||
exit(96)
|
||||
|
||||
except Exception as e:
|
||||
@@ -1073,7 +1073,7 @@ def main():
|
||||
print(" ["+str(pi)+"] "+str(port.device)+" ("+str(port.product)+", "+str(port.serial_number)+")")
|
||||
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:
|
||||
c_port = int(input())
|
||||
if c_port < 1 or c_port > len(ports):
|
||||
@@ -1206,6 +1206,11 @@ def main():
|
||||
print("---------------------------------------------------------------------------")
|
||||
print("Important! Using RNode firmware on Heltec devices should currently be")
|
||||
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("who would like to experiment with it. Hit enter to continue.")
|
||||
print("---------------------------------------------------------------------------")
|
||||
|
||||
@@ -135,6 +135,9 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None):
|
||||
if "peers" in ifstat and ifstat["peers"] != None:
|
||||
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:
|
||||
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
||||
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
||||
|
||||
+4
-1
@@ -217,7 +217,10 @@ def prettytime(time, verbose=False):
|
||||
|
||||
tstr += c
|
||||
|
||||
return tstr
|
||||
if tstr == "":
|
||||
return "0s"
|
||||
else:
|
||||
return tstr
|
||||
|
||||
def phyparams():
|
||||
print("Required Physical Layer MTU : "+str(Reticulum.MTU)+" bytes")
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "0.3.19"
|
||||
__version__ = "0.4.6"
|
||||
|
||||
+106
@@ -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,4 +1,4 @@
|
||||
# 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.
|
||||
config: 19fea9318285890fd65096600dbd8f71
|
||||
config: bb57e47d574f578f08360b71e3487c38
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -160,12 +160,13 @@ Installation
|
||||
|
||||
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>`_.
|
||||
Make sure that ``Python3`` and ``pip`` is installed on your system, and then install
|
||||
the config utility with ``pip``:
|
||||
If you have installed Reticulum on your system, the ``rnodeconf`` program will already be
|
||||
available. If not, make sure that ``Python3`` and ``pip`` is installed on your system, and
|
||||
then install Reticulum with with ``pip``:
|
||||
|
||||
.. code::
|
||||
|
||||
pip3 install rnodeconf
|
||||
pip3 install rns
|
||||
|
||||
Once installation has completed, it is time to start installing the firmware on your
|
||||
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
|
||||
questions about your hardware. Simply follow the guide, and the utility will
|
||||
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.
|
||||
auto-install and configure your devices.
|
||||
|
||||
.. _rnode-usage:
|
||||
|
||||
|
||||
@@ -771,7 +771,7 @@ Wire Format
|
||||
| | | | | | | |
|
||||
00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
@@ -786,7 +786,7 @@ Wire Format
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.3.19 beta',
|
||||
VERSION: '0.4.6 beta',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Code Examples - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Code Examples - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<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="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Index - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Index - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -139,7 +139,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -165,7 +165,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Getting Started Fast - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Getting Started Fast - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Communications Hardware - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Communications Hardware - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="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>
|
||||
<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>.
|
||||
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
|
||||
the config utility 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">rnodeconf</span>
|
||||
If you have installed Reticulum on your system, the <code class="docutils literal notranslate"><span class="pre">rnodeconf</span></code> program will already be
|
||||
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
|
||||
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>
|
||||
</div>
|
||||
<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>
|
||||
<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
|
||||
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 don’t care about stability.</p>
|
||||
auto-install and configure your devices.</p>
|
||||
</section>
|
||||
<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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Supported Interfaces - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Supported Interfaces - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Building Networks - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Building Networks - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>API Reference - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>API Reference - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="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">
|
||||
<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
|
||||
relevant interfaces. Application specific data can be added to the announce.</p>
|
||||
<dl class="field-list simple">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<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="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Search - Reticulum Network Stack 0.3.19 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.6 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/copybutton.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||
@@ -138,7 +138,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -164,7 +164,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Support Reticulum - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Support Reticulum - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="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]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
@@ -1020,7 +1020,7 @@ proof 11
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
||||
<title>What is Reticulum? - Reticulum Network Stack 0.3.19 beta documentation</title>
|
||||
<title>What is Reticulum? - Reticulum Network Stack 0.4.6 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/copybutton.css" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.3.19 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.6 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.3.19 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.6 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -160,12 +160,13 @@ Installation
|
||||
|
||||
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>`_.
|
||||
Make sure that ``Python3`` and ``pip`` is installed on your system, and then install
|
||||
the config utility with ``pip``:
|
||||
If you have installed Reticulum on your system, the ``rnodeconf`` program will already be
|
||||
available. If not, make sure that ``Python3`` and ``pip`` is installed on your system, and
|
||||
then install Reticulum with with ``pip``:
|
||||
|
||||
.. code::
|
||||
|
||||
pip3 install rnodeconf
|
||||
pip3 install rns
|
||||
|
||||
Once installation has completed, it is time to start installing the firmware on your
|
||||
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
|
||||
questions about your hardware. Simply follow the guide, and the utility will
|
||||
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.
|
||||
auto-install and configure your devices.
|
||||
|
||||
.. _rnode-usage:
|
||||
|
||||
|
||||
@@ -771,7 +771,7 @@ Wire Format
|
||||
| | | | | | | |
|
||||
00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
@@ -786,7 +786,7 @@ Wire Format
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|
||||
Reference in New Issue
Block a user