mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-06-23 12:24:30 -07:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e95e9e6a89 | |||
| e8024e560f | |||
| 8cbbcb0fe9 | |||
| 8e4bfbbd94 | |||
| 600bd0e64d | |||
| 123fd1de92 | |||
| 29df5950c8 | |||
| b8ca89c2b6 | |||
| 79725a1637 | |||
| 1a2da0d7c7 | |||
| fe065f8bdd | |||
| 5d90ea565a | |||
| b701cdd07f | |||
| 8e5b3b4e83 | |||
| 24b7cb777f | |||
| cf1ca01a3b | |||
| 7c70f9d865 | |||
| 6cf9288b11 | |||
| 00816b55bb | |||
| 3856747e31 | |||
| 50799bd2a6 | |||
| ecffa1a7eb | |||
| 9fef53d083 | |||
| 0db64610b1 | |||
| 4af14a712c | |||
| 402b5fc461 | |||
| 38aeb1ab3b | |||
| b0a21b3aa9 | |||
| 5e6a1add6b | |||
| 104b186047 | |||
| 6d23da360d | |||
| 1be00a5c41 | |||
| 71e5eef8c1 | |||
| b3a439993d | |||
| 5606b64317 | |||
| 3d38ef27d4 | |||
| 93fa8e7240 | |||
| d53e8cf037 | |||
| be820b1965 | |||
| 425cf66cf7 | |||
| 8d294df3bb | |||
| da297aeb64 | |||
| 282239fc57 | |||
| 222437d851 | |||
| c9de260e00 | |||
| 31104c6e9c | |||
| 64593e27be | |||
| bac33d4e8b | |||
| 124ec006b4 | |||
| dd55899775 | |||
| cc0c01661d | |||
| 5f36c8601f | |||
| 2f71296816 | |||
| 7923322d92 | |||
| fef5ed6bad | |||
| 059b0743ef | |||
| 4d4d39651f | |||
| 6a1e6417bb | |||
| ed20b27e9d | |||
| 39f1258d0e | |||
| 03d3478b5e | |||
| b35122a872 | |||
| ae240f4697 | |||
| 4e1cdc638f | |||
| fc83c5b082 | |||
| ee90605b30 | |||
| 3684fe502f | |||
| d4aeb85191 | |||
| 04540f6e48 | |||
| 0db7eb1408 | |||
| 5fe55243c6 | |||
| b56830b36e | |||
| e3ea61c944 | |||
| 02f9c32da7 | |||
| a4a9a1dd53 | |||
| d7f9b30638 | |||
| 02676d3b25 | |||
| 089612bfc1 | |||
| ca345b20ff | |||
| 3b5973085f | |||
| dc6877927e | |||
| f01d838e17 | |||
| 9da6d39f64 | |||
| d17fbf1f34 | |||
| 7398e312fc | |||
| 82fc8720ad | |||
| 4b9686c31a | |||
| 86a5b3302a | |||
| c990aae648 | |||
| 3051b6897d | |||
| 550dfd44cb | |||
| 95d3346da6 | |||
| d4aabc8b89 | |||
| d487609dcf | |||
| c96c82f1d1 | |||
| cb023cde40 | |||
| 17be289f37 | |||
| b8105e23ff | |||
| f378d09cbe | |||
| 4dfa62833c | |||
| 2ec6d3ba6c | |||
| 15d027e11e | |||
| 87a274d177 | |||
| f8272793b4 | |||
| 3a215be859 | |||
| 0e1279d012 | |||
| 8ec356a28e | |||
| 49d7808835 | |||
| 48184134e4 | |||
| 987ff0658b | |||
| 27dea7c524 | |||
| 9c6fd132d4 | |||
| 8d58bb62ab | |||
| c357f7a94e | |||
| 4b3ead3db2 | |||
| b62e9af5d4 | |||
| fa82989a2e | |||
| 07a65609b4 | |||
| 257bd95da8 | |||
| 1ccfa9079c | |||
| 57226201ff | |||
| d9419cd895 | |||
| aae10ede72 | |||
| 291b3056cd | |||
| 3f53c89d32 | |||
| 05288d7c97 | |||
| b403441074 | |||
| d3a23e3b00 | |||
| 329d83587e | |||
| 0a4dd64434 | |||
| b96cbf1014 | |||
| 485558cd6b | |||
| 8d93867a22 | |||
| 6b20a98adc | |||
| f3d04ba90f | |||
| 1d2564cedb | |||
| bec8473695 | |||
| 25620415a0 | |||
| b6df952995 | |||
| a72aaf12ca | |||
| b978a993b2 | |||
| 5ae00264e8 | |||
| 5396b80e80 | |||
| fdaa58a6fa |
@@ -1,6 +1,12 @@
|
||||
##########################################################
|
||||
# This RNS example demonstrates a simple speedtest #
|
||||
# program to measure link throughput. #
|
||||
# #
|
||||
# The current configuration is suited for testing fast #
|
||||
# links. If you want to measure slow links like LoRa or #
|
||||
# packet radio, you must significantly lower the #
|
||||
# data_cap variable, which defines how much data is sent #
|
||||
# for each test. #
|
||||
##########################################################
|
||||
|
||||
import os
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License, unless otherwise noted
|
||||
|
||||
Copyright (c) 2018 Mark Qvist / unsigned.io
|
||||
Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
Reticulum Network Stack β
|
||||
==========
|
||||
|
||||
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
|
||||
<p align="center"><img width="200" src="https://unsigned.io/wp-content/uploads/2022/03/reticulum_logo_512.png"></p>
|
||||
|
||||
Reticulum is the cryptography-based networking stack for wide-area networks built on readily available hardware. It can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build wide-area networks with off-the-shelf tools, and offers end-to-end encryption and connectivity, initiator anonymity, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable delivery acknowledgements and more.
|
||||
|
||||
The vision of Reticulum is to allow anyone to be their own network operator, and to make it cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks. Reticulum **is not** *one network*, it **is a tool** for building *thousands of networks*. Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
|
||||
Reticulum is a complete networking stack, and does not need IP or higher layers, although it is easy to use IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks.
|
||||
|
||||
@@ -9,6 +13,7 @@ Having no dependencies on traditional networking stacks free up overhead that ha
|
||||
|
||||
No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.
|
||||
|
||||
## Read The Manual
|
||||
The full documentation for Reticulum is available at [markqvist.github.io/Reticulum/manual/](https://markqvist.github.io/Reticulum/manual/).
|
||||
|
||||
You can also [download the Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf)
|
||||
@@ -16,33 +21,34 @@ You can also [download the Reticulum manual as a PDF](https://github.com/markqvi
|
||||
For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects/reticulum/)
|
||||
|
||||
## Notable Features
|
||||
- Coordination-less globally unique adressing and identification
|
||||
- Fully self-configuring multi-hop routing
|
||||
- Complete initiator anonymity, communicate without revealing your identity
|
||||
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
||||
- Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for on-the-wire / over-the-air encryption
|
||||
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
|
||||
- AES-128 in CBC mode with PKCS7 padding
|
||||
- HMAC using SHA256 for authentication
|
||||
- IVs are generated through os.urandom()
|
||||
- Unforgeable packet delivery confirmations
|
||||
- A variety of supported interface types
|
||||
- An intuitive and easy-to-use API
|
||||
- Reliable and efficient transfer of arbritrary amounts of data
|
||||
- Reticulum can handle a few bytes of data or files of many gigabytes
|
||||
- Sequencing, transfer coordination and checksumming is automatic
|
||||
- The API is very easy to use, and provides transfer progress
|
||||
- Lightweight, flexible and expandable Request/Response mechanism
|
||||
- Efficient link establishment
|
||||
- Total bandwidth cost of setting up a link is 3 packets totalling 237 bytes
|
||||
- Low cost of keeping links open at only 0.62 bits per second
|
||||
- Coordination-less globally unique adressing and identification
|
||||
- Fully self-configuring multi-hop routing
|
||||
- Complete initiator anonymity, communicate without revealing your identity
|
||||
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
||||
- Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for on-the-wire / over-the-air encryption
|
||||
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
|
||||
- AES-128 in CBC mode with PKCS7 padding
|
||||
- HMAC using SHA256 for authentication
|
||||
- IVs are generated through os.urandom()
|
||||
- Unforgeable packet delivery confirmations
|
||||
- A variety of supported interface types
|
||||
- An intuitive and easy-to-use API
|
||||
- Reliable and efficient transfer of arbritrary amounts of data
|
||||
- Reticulum can handle a few bytes of data or files of many gigabytes
|
||||
- Sequencing, transfer coordination and checksumming is automatic
|
||||
- The API is very easy to use, and provides transfer progress
|
||||
- Lightweight, flexible and expandable Request/Response mechanism
|
||||
- Efficient link establishment
|
||||
- Total bandwidth cost of setting up an encrypted link is 3 packets totalling 237 bytes
|
||||
- Low cost of keeping links open at only 0.62 bits per second
|
||||
|
||||
## Examples of Reticulum Applications
|
||||
If you want to quickly get an idea of what Reticulum can do, take a look at the following resources.
|
||||
|
||||
- For an off-grid, encrypted and resilient mesh communications platform, see [Nomad Network](https://github.com/markqvist/NomadNet)
|
||||
- For a distributed, delay and disruption tolerant message transfer protocol built on Reticulum, see [LXMF](https://github.com/markqvist/lxmf)
|
||||
- [LXMF](https://github.com/markqvist/lxmf) is a distributed, delay and disruption tolerant message transfer protocol built on Reticulum
|
||||
- For an off-grid, encrypted and resilient mesh communications platform, see [Nomad Network](https://github.com/markqvist/NomadNet)
|
||||
- The Android, Linux and macOS app [Sideband](https://unsigned.io/sideband) has a graphical interface and focuses on ease of use.
|
||||
|
||||
## Where can Reticulum be used?
|
||||
Over practically any medium that can support at least a half-duplex channel with 500 bits per second throughput, and an MTU of 500 bytes. Data radios, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, ad-hoc WiFi, free-space optical links and similar systems are all examples of the types of interfaces Reticulum was designed for.
|
||||
@@ -74,36 +80,75 @@ Reticulum should currently be considered beta software. All core protocol featur
|
||||
|
||||
## Supported interface types and devices
|
||||
|
||||
Reticulum implements a range of generalised interface types that covers most of the communications hardware that Reticulum can run over. If your hardware is not supported, it's relatively simple to implement an interface class. Currently, the following interfaces are supported:
|
||||
Reticulum implements a range of generalised interface types that covers most of the communications hardware that Reticulum can run over. If your hardware is not supported, it's relatively simple to implement an interface class. I will gratefully accept pull requests for custom interfaces if they are generally useful.
|
||||
|
||||
- Any ethernet device
|
||||
- LoRa using [RNode](https://unsigned.io/projects/rnode/)
|
||||
- Packet Radio TNCs (with or without AX.25)
|
||||
- Any device with a serial port
|
||||
- TCP over IP networks
|
||||
- UDP over IP networks
|
||||
Currently, the following interfaces are supported:
|
||||
|
||||
- Any ethernet device
|
||||
- LoRa using [RNode](https://unsigned.io/projects/rnode/)
|
||||
- Packet Radio TNCs (with or without AX.25)
|
||||
- KISS-compatible hardware and software modems
|
||||
- Any device with a serial port
|
||||
- TCP over IP networks
|
||||
- UDP over IP networks
|
||||
|
||||
## Development Roadmap
|
||||
- Version 0.3.6
|
||||
- Improving [the manual](https://markqvist.github.io/Reticulum/manual/) with sections specifically for beginners
|
||||
- Support for radio and modem interfaces on Android
|
||||
- GUI interface configuration tool
|
||||
- Easy way to share interface configurations, see [#19](https://github.com/markqvist/Reticulum/discussions/19)
|
||||
- Version 0.3.7
|
||||
- More interface types for even broader compatibility
|
||||
- Plain ESP32 devices (ESP-Now, WiFi, Bluetooth, etc.)
|
||||
- More LoRa transceivers
|
||||
- AT-compatible modems
|
||||
- IR Transceivers
|
||||
- AWDL / OWL
|
||||
- HF Modems
|
||||
- CAN-bus
|
||||
- ZeroMQ
|
||||
- MQTT
|
||||
- SPI
|
||||
- i²c
|
||||
- Planned, but not yet scheduled
|
||||
- Globally routable multicast
|
||||
- A portable Reticulum implementation in C, see [#21](https://github.com/markqvist/Reticulum/discussions/21)
|
||||
|
||||
|
||||
|
||||
## Feature Roadmap
|
||||
- Stream mode for links
|
||||
- Globally routable multicast
|
||||
- More interface types for even broader compatibility
|
||||
- ESP32 devices (ESP-Now, Bluetooth, etc.)
|
||||
- More LoRa transceivers
|
||||
- AT-compatible modems
|
||||
- AWDL / OWL
|
||||
- CAN-bus
|
||||
- ZeroMQ
|
||||
- MQTT
|
||||
- SPI
|
||||
- i²c
|
||||
|
||||
## Dependencies:
|
||||
- Python 3.6
|
||||
- cryptography.io
|
||||
- netifaces
|
||||
- pyserial
|
||||
- Python 3.6
|
||||
- cryptography.io
|
||||
- netifaces
|
||||
- pyserial
|
||||
|
||||
## Public Testnet
|
||||
|
||||
If you just want to get started experimenting without building any physical networks, you are welcome to join the Unsigned.io RNS Testnet. The testnet is just that, an informal network for testing and experimenting. It will be up most of the time, and anyone can join, but it also means that there's no guarantees for service availability.
|
||||
|
||||
The testnet runs the very latest version of Reticulum (often even a short while before it is publicly released). Sometimes experimental versions of Reticulum might be deployed to nodes on the testnet, which means strange behaviour might occur. If none of that scares you, you can join the testnet via eihter TCP or I2P. Just add one of the following interfaces to your Reticulum configuration file:
|
||||
|
||||
```
|
||||
# For connecting over TCP/IP:
|
||||
|
||||
[[RNS Testnet Frankfurt]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = yes
|
||||
outgoing = True
|
||||
target_host = frankfurt.rns.unsigned.io
|
||||
target_port = 4965
|
||||
|
||||
|
||||
# For connecting over I2P:
|
||||
|
||||
[[RNS Testnet I2P Node A]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
peers = ykzlw5ujbaqc2xkec4cpvgyxj257wcrmmgkuxqmqcur7cq3w3lha.b32.i2p
|
||||
```
|
||||
|
||||
The testnet also contains a number of [Nomad Network](https://github.com/markqvist/nomadnet) nodes, and LXMF propagation nodes.
|
||||
|
||||
## Support Reticulum
|
||||
You can help support the continued development of open, free and private communications systems by donating via one of the following channels:
|
||||
@@ -112,5 +157,7 @@ You can help support the continued development of open, free and private communi
|
||||
- Bitcoin: 3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq
|
||||
- Ko-Fi: https://ko-fi.com/markqvist
|
||||
|
||||
Are certain features in the development roadmap are important to you or your organisation? Make them a reality quickly by sponsoring their implementation.
|
||||
|
||||
## Caveat Emptor
|
||||
Reticulum is experimental software, and should be considered as such. While it has been built with cryptography best-practices very foremost in mind, it _has not_ been externally security audited, and there could very well be privacy-breaking bugs. If you want to help out, or help sponsor an audit, please do get in touch.
|
||||
Reticulum is relatively young software, and should be considered as such. While it has been built with cryptography best-practices very foremost in mind, it _has not_ been externally security audited, and there could very well be privacy-breaking bugs. If you want to help out, or help sponsor an audit, please do get in touch.
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import base64
|
||||
import math
|
||||
import time
|
||||
@@ -118,6 +140,9 @@ class Destination:
|
||||
identity = RNS.Identity()
|
||||
aspects = aspects+(identity.hexhash,)
|
||||
|
||||
if identity != None and self.type == Destination.PLAIN:
|
||||
raise TypeError("Selected destination type PLAIN cannot hold an identity")
|
||||
|
||||
self.identity = identity
|
||||
|
||||
self.name = Destination.full_name(app_name, *aspects)
|
||||
@@ -146,6 +171,9 @@ class Destination:
|
||||
:param app_data: *bytes* containing the app_data.
|
||||
:param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore.
|
||||
"""
|
||||
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")
|
||||
|
||||
|
||||
+73
-32
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import base64
|
||||
import math
|
||||
import os
|
||||
@@ -14,6 +36,8 @@ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X
|
||||
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
cio_default_backend = default_backend()
|
||||
|
||||
class Identity:
|
||||
"""
|
||||
This class is used to manage identities in Reticulum. It provides methods
|
||||
@@ -34,11 +58,12 @@ class Identity:
|
||||
"""
|
||||
|
||||
# Non-configurable constants
|
||||
FERNET_VERSION = 0x80
|
||||
FERNET_OVERHEAD = 54 # In bytes
|
||||
AES128_BLOCKSIZE = 16 # In bytes
|
||||
HASHLENGTH = 256 # In bits
|
||||
SIGLENGTH = KEYSIZE # In bits
|
||||
FERNET_VERSION = 0x80
|
||||
FERNET_OVERHEAD = 57 # In bytes
|
||||
OPTIMISED_FERNET_OVERHEAD = 54 # In bytes
|
||||
AES128_BLOCKSIZE = 16 # In bytes
|
||||
HASHLENGTH = 256 # In bits
|
||||
SIGLENGTH = KEYSIZE # In bits
|
||||
|
||||
TRUNCATED_HASHLENGTH = RNS.Reticulum.TRUNCATED_HASHLENGTH
|
||||
"""
|
||||
@@ -156,37 +181,47 @@ class Identity:
|
||||
:param data: Data to be hashed as *bytes*.
|
||||
:returns: Truncated SHA-256 hash of random data as *bytes*
|
||||
"""
|
||||
return Identity.truncated_hash(os.urandom(10))
|
||||
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
|
||||
|
||||
@staticmethod
|
||||
def validate_announce(packet):
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG)
|
||||
destination_hash = packet.destination_hash
|
||||
public_key = packet.data[:Identity.KEYSIZE//8]
|
||||
random_hash = packet.data[Identity.KEYSIZE//8:Identity.KEYSIZE//8+10]
|
||||
signature = packet.data[Identity.KEYSIZE//8+10:Identity.KEYSIZE//8+10+Identity.KEYSIZE//8]
|
||||
app_data = b""
|
||||
if len(packet.data) > Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:
|
||||
app_data = packet.data[Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:]
|
||||
try:
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
destination_hash = packet.destination_hash
|
||||
public_key = packet.data[:Identity.KEYSIZE//8]
|
||||
random_hash = packet.data[Identity.KEYSIZE//8:Identity.KEYSIZE//8+10]
|
||||
signature = packet.data[Identity.KEYSIZE//8+10:Identity.KEYSIZE//8+10+Identity.KEYSIZE//8]
|
||||
app_data = b""
|
||||
if len(packet.data) > Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:
|
||||
app_data = packet.data[Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:]
|
||||
|
||||
signed_data = destination_hash+public_key+random_hash+app_data
|
||||
signed_data = destination_hash+public_key+random_hash+app_data
|
||||
|
||||
if not len(packet.data) > Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:
|
||||
app_data = None
|
||||
if not len(packet.data) > Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:
|
||||
app_data = None
|
||||
|
||||
announced_identity = Identity(create_keys=False)
|
||||
announced_identity.load_public_key(public_key)
|
||||
announced_identity = Identity(create_keys=False)
|
||||
announced_identity.load_public_key(public_key)
|
||||
|
||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||
RNS.Identity.remember(packet.get_hash(), destination_hash, public_key, app_data)
|
||||
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
|
||||
del announced_identity
|
||||
return True
|
||||
else:
|
||||
RNS.log("Received invalid announce", RNS.LOG_DEBUG)
|
||||
del announced_identity
|
||||
return False
|
||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||
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)
|
||||
else:
|
||||
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface), RNS.LOG_EXTREME)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
RNS.log("Received invalid announce for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
|
||||
del announced_identity
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred while validating announce. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def exit_handler():
|
||||
@@ -392,11 +427,14 @@ class Identity:
|
||||
)
|
||||
|
||||
shared_key = ephemeral_key.exchange(self.pub)
|
||||
derived_key = derived_key = HKDF(
|
||||
|
||||
# TODO: Improve this re-allocation of HKDF
|
||||
derived_key = HKDF(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=self.get_salt(),
|
||||
info=self.get_context(),
|
||||
backend=cio_default_backend,
|
||||
).derive(shared_key)
|
||||
|
||||
fernet = Fernet(base64.urlsafe_b64encode(derived_key))
|
||||
@@ -424,11 +462,14 @@ class Identity:
|
||||
peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
|
||||
|
||||
shared_key = self.prv.exchange(peer_pub)
|
||||
derived_key = derived_key = HKDF(
|
||||
|
||||
# TODO: Improve this re-allocation of HKDF
|
||||
derived_key = HKDF(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=self.get_salt(),
|
||||
info=self.get_context(),
|
||||
backend=cio_default_backend,
|
||||
).derive(shared_key)
|
||||
|
||||
fernet = Fernet(base64.urlsafe_b64encode(derived_key))
|
||||
@@ -459,7 +500,7 @@ class Identity:
|
||||
return self.sig_prv.sign(message)
|
||||
except Exception as e:
|
||||
RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
raise e
|
||||
raise e
|
||||
else:
|
||||
raise KeyError("Signing failed because identity does not hold a private key")
|
||||
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
from time import sleep
|
||||
@@ -37,6 +58,7 @@ class AX25():
|
||||
|
||||
class AX25KISSInterface(Interface):
|
||||
MAX_CHUNK = 32768
|
||||
BITRATE_GUESS = 1200
|
||||
|
||||
owner = None
|
||||
port = None
|
||||
@@ -73,6 +95,7 @@ class AX25KISSInterface(Interface):
|
||||
self.stopbits = stopbits
|
||||
self.timeout = 100
|
||||
self.online = False
|
||||
self.bitrate = KISSInterface.BITRATE_GUESS
|
||||
|
||||
self.packet_queue = []
|
||||
self.flow_control = flow_control
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
import socketserver
|
||||
import threading
|
||||
@@ -19,11 +41,14 @@ class AutoInterface(Interface):
|
||||
SCOPE_ORGANISATION = "8"
|
||||
SCOPE_GLOBAL = "e"
|
||||
|
||||
PEERING_TIMEOUT = 6.0
|
||||
PEERING_TIMEOUT = 7.5
|
||||
|
||||
DARWIN_IGNORE_IFS = ["awdl0", "llw0", "lo0", "en5"]
|
||||
ANDROID_IGNORE_IFS = ["dummy0", "lo", "tun0"]
|
||||
|
||||
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None):
|
||||
BITRATE_GUESS = 10*1000*1000
|
||||
|
||||
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:
|
||||
import netifaces
|
||||
@@ -42,12 +67,15 @@ class AutoInterface(Interface):
|
||||
self.peers = {}
|
||||
self.link_local_addresses = []
|
||||
self.adopted_interfaces = {}
|
||||
self.multicast_echoes = {}
|
||||
self.timed_out_interfaces = {}
|
||||
|
||||
self.outbound_udp_socket = None
|
||||
|
||||
self.announce_interval = AutoInterface.PEERING_TIMEOUT/4.0
|
||||
self.announce_interval = AutoInterface.PEERING_TIMEOUT/6.0
|
||||
self.peer_job_interval = AutoInterface.PEERING_TIMEOUT*1.1
|
||||
self.peering_timeout = AutoInterface.PEERING_TIMEOUT
|
||||
self.multicast_echo_timeout = AutoInterface.PEERING_TIMEOUT/2
|
||||
|
||||
if allowed_interfaces == None:
|
||||
self.allowed_interfaces = []
|
||||
@@ -101,10 +129,12 @@ class AutoInterface(Interface):
|
||||
|
||||
suitable_interfaces = 0
|
||||
for ifname in self.netifaces.interfaces():
|
||||
if RNS.vendor.platformutils.get_platform() == "darwin" and ifname in AutoInterface.DARWIN_IGNORE_IFS:
|
||||
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
|
||||
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME)
|
||||
elif RNS.vendor.platformutils.get_platform() == "darwin" and ifname == "lo0":
|
||||
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
|
||||
RNS.log(str(self)+" skipping Darwin loopback interface "+str(ifname), RNS.LOG_EXTREME)
|
||||
elif RNS.vendor.platformutils.is_android() and ifname in AutoInterface.ANDROID_IGNORE_IFS and not ifname in self.allowed_interfaces:
|
||||
RNS.log(str(self)+" skipping Android system interface "+str(ifname), RNS.LOG_EXTREME)
|
||||
elif ifname in self.ignored_interfaces:
|
||||
RNS.log(str(self)+" ignoring disallowed interface "+str(ifname), RNS.LOG_EXTREME)
|
||||
else:
|
||||
@@ -120,6 +150,7 @@ class AutoInterface(Interface):
|
||||
link_local_addr = address["addr"]
|
||||
self.link_local_addresses.append(link_local_addr.split("%")[0])
|
||||
self.adopted_interfaces[ifname] = link_local_addr.split("%")[0]
|
||||
self.multicast_echoes[ifname] = time.time()
|
||||
RNS.log(str(self)+" Selecting link-local address "+str(link_local_addr)+" for interface "+str(ifname), RNS.LOG_EXTREME)
|
||||
|
||||
if link_local_addr == None:
|
||||
@@ -188,6 +219,11 @@ class AutoInterface(Interface):
|
||||
|
||||
time.sleep(peering_wait)
|
||||
|
||||
if configured_bitrate != None:
|
||||
self.bitrate = configured_bitrate
|
||||
else:
|
||||
self.bitrate = AutoInterface.BITRATE_GUESS
|
||||
|
||||
self.online = True
|
||||
|
||||
|
||||
@@ -212,15 +248,32 @@ class AutoInterface(Interface):
|
||||
time.sleep(self.peer_job_interval)
|
||||
now = time.time()
|
||||
timed_out_peers = []
|
||||
|
||||
# Check for timed out peers
|
||||
for peer_addr in self.peers:
|
||||
peer = self.peers[peer_addr]
|
||||
last_heard = peer[1]
|
||||
if now > last_heard+self.peering_timeout:
|
||||
timed_out_peers.append(peer_addr)
|
||||
|
||||
# Remove any timed out peers
|
||||
for peer_addr in timed_out_peers:
|
||||
removed_peer = self.peers.pop(peer_addr)
|
||||
RNS.log(str(self)+" removed peer "+str(peer_addr)+" on "+str(removed_peer[0]), RNS.LOG_DEBUG)
|
||||
|
||||
for ifname in self.adopted_interfaces:
|
||||
last_multicast_echo = 0
|
||||
if ifname in self.multicast_echoes:
|
||||
last_multicast_echo = self.multicast_echoes[ifname]
|
||||
|
||||
if now - last_multicast_echo > self.multicast_echo_timeout:
|
||||
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False:
|
||||
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:
|
||||
RNS.log(str(self)+" Carrier recovered on "+str(ifname), RNS.LOG_WARNING)
|
||||
self.timed_out_interfaces[ifname] = False
|
||||
|
||||
|
||||
def announce_handler(self, ifname):
|
||||
@@ -229,17 +282,34 @@ class AutoInterface(Interface):
|
||||
time.sleep(self.announce_interval)
|
||||
|
||||
def peer_announce(self, ifname):
|
||||
link_local_address = self.adopted_interfaces[ifname]
|
||||
discovery_token = RNS.Identity.full_hash(self.group_id+link_local_address.encode("utf-8"))
|
||||
announce_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
addr_info = socket.getaddrinfo(self.mcast_discovery_address, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
try:
|
||||
link_local_address = self.adopted_interfaces[ifname]
|
||||
discovery_token = RNS.Identity.full_hash(self.group_id+link_local_address.encode("utf-8"))
|
||||
announce_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
addr_info = socket.getaddrinfo(self.mcast_discovery_address, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
|
||||
ifis = struct.pack("I", socket.if_nametoindex(ifname))
|
||||
announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)
|
||||
announce_socket.sendto(discovery_token, addr_info[0][4])
|
||||
ifis = struct.pack("I", socket.if_nametoindex(ifname))
|
||||
announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)
|
||||
announce_socket.sendto(discovery_token, addr_info[0][4])
|
||||
except Exception as e:
|
||||
if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces:
|
||||
RNS.log(str(self)+" Detected possible carrier loss on "+str(ifname)+": "+str(e), RNS.LOG_WARNING)
|
||||
else:
|
||||
pass
|
||||
|
||||
def add_peer(self, addr, ifname):
|
||||
if not addr in self.link_local_addresses:
|
||||
if addr in self.link_local_addresses:
|
||||
ifname = None
|
||||
for interface_name in self.adopted_interfaces:
|
||||
if self.adopted_interfaces[interface_name] == addr:
|
||||
ifname = interface_name
|
||||
|
||||
if ifname != None:
|
||||
self.multicast_echoes[ifname] = time.time()
|
||||
else:
|
||||
RNS.log(str(self)+" received multicast echo on unexpected interface "+str(ifname), RNS.LOG_WARNING)
|
||||
|
||||
else:
|
||||
if not addr in self.peers:
|
||||
self.peers[addr] = [ifname, time.time()]
|
||||
RNS.log(str(self)+" added peer "+str(addr)+" on "+str(ifname), RNS.LOG_DEBUG)
|
||||
|
||||
@@ -0,0 +1,622 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
import socketserver
|
||||
import threading
|
||||
import platform
|
||||
import socket
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import RNS
|
||||
import asyncio
|
||||
|
||||
class HDLC():
|
||||
FLAG = 0x7E
|
||||
ESC = 0x7D
|
||||
ESC_MASK = 0x20
|
||||
|
||||
@staticmethod
|
||||
def escape(data):
|
||||
data = data.replace(bytes([HDLC.ESC]), bytes([HDLC.ESC, HDLC.ESC^HDLC.ESC_MASK]))
|
||||
data = data.replace(bytes([HDLC.FLAG]), bytes([HDLC.ESC, HDLC.FLAG^HDLC.ESC_MASK]))
|
||||
return data
|
||||
|
||||
class KISS():
|
||||
FEND = 0xC0
|
||||
FESC = 0xDB
|
||||
TFEND = 0xDC
|
||||
TFESC = 0xDD
|
||||
CMD_DATA = 0x00
|
||||
CMD_UNKNOWN = 0xFE
|
||||
|
||||
@staticmethod
|
||||
def escape(data):
|
||||
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
|
||||
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
|
||||
return data
|
||||
|
||||
# TODO: Neater shutdown of the event loop and
|
||||
# better error handling is needed. Sometimes
|
||||
# errors occur in I2P that leave tunnel setup
|
||||
# hanging indefinitely, and right now we have
|
||||
# no way of catching it. Sometimes the server
|
||||
# and client tasks are also not cancelled on
|
||||
# shutdown, which leads to errors dumped to
|
||||
# the console. This should also be remedied.
|
||||
|
||||
class I2PController:
|
||||
def __init__(self, rns_storagepath):
|
||||
import RNS.vendor.i2plib as i2plib
|
||||
import RNS.vendor.i2plib.utils
|
||||
|
||||
self.client_tunnels = {}
|
||||
self.server_tunnels = {}
|
||||
self.loop = None
|
||||
self.i2plib = i2plib
|
||||
self.utils = i2plib.utils
|
||||
self.sam_address = i2plib.get_sam_address()
|
||||
|
||||
self.storagepath = rns_storagepath+"/i2p"
|
||||
if not os.path.isdir(self.storagepath):
|
||||
os.makedirs(self.storagepath)
|
||||
|
||||
|
||||
def start(self):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
self.loop = asyncio.get_event_loop()
|
||||
try:
|
||||
self.loop.run_forever()
|
||||
except Exception as e:
|
||||
RNS.log("Exception on event loop for "+str(self)+": "+str(e), RNS.LOG_ERROR)
|
||||
finally:
|
||||
self.loop.close()
|
||||
|
||||
|
||||
def stop(self):
|
||||
for task in asyncio.Task.all_tasks(loop=self.loop):
|
||||
task.cancel()
|
||||
|
||||
self.loop.stop()
|
||||
|
||||
|
||||
def get_free_port(self):
|
||||
return self.i2plib.utils.get_free_port()
|
||||
|
||||
|
||||
def client_tunnel(self, owner, i2p_destination):
|
||||
self.client_tunnels[i2p_destination] = False
|
||||
|
||||
while True:
|
||||
if not self.client_tunnels[i2p_destination]:
|
||||
try:
|
||||
async def tunnel_up():
|
||||
RNS.log("Bringing up I2P tunnel to "+str(owner)+", this may take a while...", RNS.LOG_INFO)
|
||||
tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop)
|
||||
await tunnel.run()
|
||||
owner.awaiting_i2p_tunnel = False
|
||||
RNS.log(str(owner)+ " tunnel setup complete", RNS.LOG_VERBOSE)
|
||||
|
||||
try:
|
||||
self.loop.ext_owner = self
|
||||
future = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||
self.client_tunnels[i2p_destination] = True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while setting up I2P tunnel: "+str(e))
|
||||
raise e
|
||||
|
||||
|
||||
except Exception as e:
|
||||
raise IOError("Could not connect to I2P SAM API while configuring to "+str(owner)+". Check that I2P is running and SAM is enabled.")
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
def server_tunnel(self, owner):
|
||||
i2p_dest_hash = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8")))
|
||||
i2p_keyfile = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash, delimit=False)+".i2p"
|
||||
|
||||
i2p_dest = None
|
||||
if not os.path.isfile(i2p_keyfile):
|
||||
coro = self.i2plib.new_destination(sam_address=self.sam_address, loop=self.loop)
|
||||
i2p_dest = asyncio.run_coroutine_threadsafe(coro, self.loop).result()
|
||||
key_file = open(i2p_keyfile, "w")
|
||||
key_file.write(i2p_dest.private_key.base64)
|
||||
key_file.close()
|
||||
else:
|
||||
key_file = open(i2p_keyfile, "r")
|
||||
prvd = key_file.read()
|
||||
key_file.close()
|
||||
i2p_dest = self.i2plib.Destination(data=prvd, has_private_key=True)
|
||||
|
||||
i2p_b32 = i2p_dest.base32
|
||||
owner.b32 = i2p_b32
|
||||
|
||||
self.server_tunnels[i2p_b32] = False
|
||||
|
||||
while self.server_tunnels[i2p_b32] == False:
|
||||
try:
|
||||
async def tunnel_up():
|
||||
RNS.log(str(owner)+" Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
|
||||
tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address)
|
||||
await tunnel.run()
|
||||
RNS.log(str(owner)+ " endpoint setup complete. Now reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE)
|
||||
|
||||
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||
self.server_tunnels[i2p_b32] = True
|
||||
|
||||
except Exception as e:
|
||||
raise IOError("Could not connect to I2P SAM API while configuring "+str(self)+". Check that I2P is running and SAM is enabled.")
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
def get_loop(self):
|
||||
return asyncio.get_event_loop()
|
||||
|
||||
|
||||
class ThreadingI2PServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
class I2PInterfacePeer(Interface):
|
||||
RECONNECT_WAIT = 15
|
||||
RECONNECT_MAX_TRIES = None
|
||||
|
||||
# TCP socket options
|
||||
I2P_USER_TIMEOUT = 40
|
||||
I2P_PROBE_AFTER = 10
|
||||
I2P_PROBE_INTERVAL = 5
|
||||
I2P_PROBES = 6
|
||||
|
||||
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
self.socket = None
|
||||
self.parent_interface = parent_interface
|
||||
self.parent_count = True
|
||||
self.name = name
|
||||
self.initiator = False
|
||||
self.reconnecting = False
|
||||
self.never_connected = True
|
||||
self.owner = owner
|
||||
self.writing = False
|
||||
self.online = False
|
||||
self.detached = False
|
||||
self.kiss_framing = False
|
||||
self.i2p_tunneled = True
|
||||
self.i2p_dest = None
|
||||
self.i2p_tunnel_ready = False
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
self.bitrate = I2PInterface.BITRATE_GUESS
|
||||
|
||||
if max_reconnect_tries == None:
|
||||
self.max_reconnect_tries = I2PInterfacePeer.RECONNECT_MAX_TRIES
|
||||
else:
|
||||
self.max_reconnect_tries = max_reconnect_tries
|
||||
|
||||
if connected_socket != None:
|
||||
self.receives = True
|
||||
self.target_ip = None
|
||||
self.target_port = None
|
||||
self.socket = connected_socket
|
||||
|
||||
if platform.system() == "Linux":
|
||||
self.set_timeouts_linux()
|
||||
elif platform.system() == "Darwin":
|
||||
self.set_timeouts_osx()
|
||||
|
||||
elif target_i2p_dest != None:
|
||||
self.receives = True
|
||||
self.initiator = True
|
||||
|
||||
self.bind_ip = "127.0.0.1"
|
||||
self.bind_port = self.parent_interface.i2p.get_free_port()
|
||||
self.local_addr = (self.bind_ip, self.bind_port)
|
||||
self.target_ip = self.bind_ip
|
||||
self.target_port = self.bind_port
|
||||
|
||||
self.awaiting_i2p_tunnel = True
|
||||
|
||||
def tunnel_job():
|
||||
self.parent_interface.i2p.client_tunnel(self, target_i2p_dest)
|
||||
|
||||
thread = threading.Thread(target=tunnel_job)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
def wait_job():
|
||||
while self.awaiting_i2p_tunnel:
|
||||
time.sleep(0.25)
|
||||
|
||||
if not self.kiss_framing:
|
||||
self.wants_tunnel = True
|
||||
|
||||
if not self.connect(initial=True):
|
||||
thread = threading.Thread(target=self.reconnect)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
else:
|
||||
thread = threading.Thread(target=self.read_loop)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
thread = threading.Thread(target=wait_job)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
|
||||
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))
|
||||
|
||||
def set_timeouts_osx(self):
|
||||
if hasattr(socket, "TCP_KEEPALIVE"):
|
||||
TCP_KEEPIDLE = socket.TCP_KEEPALIVE
|
||||
else:
|
||||
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))
|
||||
|
||||
def detach(self):
|
||||
if self.socket != None:
|
||||
if hasattr(self.socket, "close"):
|
||||
if callable(self.socket.close):
|
||||
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
|
||||
self.detached = True
|
||||
|
||||
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))
|
||||
|
||||
self.socket = None
|
||||
|
||||
def connect(self, initial=False):
|
||||
try:
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.connect((self.target_ip, self.target_port))
|
||||
self.online = True
|
||||
|
||||
except Exception as e:
|
||||
if initial:
|
||||
if not self.awaiting_i2p_tunnel:
|
||||
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log("Leaving unconnected and retrying connection in "+str(I2PInterfacePeer.RECONNECT_WAIT)+" seconds.", RNS.LOG_ERROR)
|
||||
|
||||
return False
|
||||
|
||||
else:
|
||||
raise e
|
||||
|
||||
if platform.system() == "Linux":
|
||||
self.set_timeouts_linux()
|
||||
elif platform.system() == "Darwin":
|
||||
self.set_timeouts_osx()
|
||||
|
||||
self.online = True
|
||||
self.writing = False
|
||||
self.never_connected = False
|
||||
|
||||
if not self.kiss_framing and self.wants_tunnel:
|
||||
RNS.Transport.synthesize_tunnel(self)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def reconnect(self):
|
||||
if self.initiator:
|
||||
if not self.reconnecting:
|
||||
self.reconnecting = True
|
||||
attempts = 0
|
||||
while not self.online:
|
||||
time.sleep(I2PInterfacePeer.RECONNECT_WAIT)
|
||||
attempts += 1
|
||||
|
||||
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries:
|
||||
RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR)
|
||||
self.teardown()
|
||||
break
|
||||
|
||||
try:
|
||||
self.connect()
|
||||
|
||||
except Exception as e:
|
||||
if not self.awaiting_i2p_tunnel:
|
||||
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log(str(self)+" still waiting for I2P tunnel to appear", RNS.LOG_VERBOSE)
|
||||
|
||||
if not self.never_connected:
|
||||
RNS.log(str(self)+" Re-established connection via I2P tunnel", RNS.LOG_INFO)
|
||||
|
||||
self.reconnecting = False
|
||||
thread = threading.Thread(target=self.read_loop)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
if not self.kiss_framing:
|
||||
RNS.Transport.synthesize_tunnel(self)
|
||||
|
||||
else:
|
||||
RNS.log("Attempt to reconnect on a non-initiator I2P interface. This should not happen.", RNS.LOG_ERROR)
|
||||
raise IOError("Attempt to reconnect on a non-initiator I2P interface")
|
||||
|
||||
def processIncoming(self, data):
|
||||
self.rxb += len(data)
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
||||
self.parent_interface.rxb += len(data)
|
||||
|
||||
self.owner.inbound(data, self)
|
||||
|
||||
def processOutgoing(self, data):
|
||||
if self.online:
|
||||
while self.writing:
|
||||
time.sleep(0.01)
|
||||
|
||||
try:
|
||||
self.writing = True
|
||||
|
||||
if self.kiss_framing:
|
||||
data = bytes([KISS.FEND])+bytes([KISS.CMD_DATA])+KISS.escape(data)+bytes([KISS.FEND])
|
||||
else:
|
||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||
|
||||
self.socket.sendall(data)
|
||||
self.writing = False
|
||||
self.txb += len(data)
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
||||
self.parent_interface.txb += len(data)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
self.teardown()
|
||||
|
||||
|
||||
def read_loop(self):
|
||||
try:
|
||||
in_frame = False
|
||||
escape = False
|
||||
data_buffer = b""
|
||||
command = KISS.CMD_UNKNOWN
|
||||
|
||||
while True:
|
||||
data_in = self.socket.recv(4096)
|
||||
if len(data_in) > 0:
|
||||
pointer = 0
|
||||
while pointer < len(data_in):
|
||||
byte = data_in[pointer]
|
||||
pointer += 1
|
||||
|
||||
if self.kiss_framing:
|
||||
# Read loop for KISS framing
|
||||
if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA):
|
||||
in_frame = False
|
||||
self.processIncoming(data_buffer)
|
||||
elif (byte == KISS.FEND):
|
||||
in_frame = True
|
||||
command = KISS.CMD_UNKNOWN
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
|
||||
# We only support one HDLC port for now, so
|
||||
# strip off the port nibble
|
||||
byte = byte & 0x0F
|
||||
command = byte
|
||||
elif (command == KISS.CMD_DATA):
|
||||
if (byte == KISS.FESC):
|
||||
escape = True
|
||||
else:
|
||||
if (escape):
|
||||
if (byte == KISS.TFEND):
|
||||
byte = KISS.FEND
|
||||
if (byte == KISS.TFESC):
|
||||
byte = KISS.FESC
|
||||
escape = False
|
||||
data_buffer = data_buffer+bytes([byte])
|
||||
|
||||
else:
|
||||
# Read loop for HDLC framing
|
||||
if (in_frame and byte == HDLC.FLAG):
|
||||
in_frame = False
|
||||
self.processIncoming(data_buffer)
|
||||
elif (byte == HDLC.FLAG):
|
||||
in_frame = True
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
if (byte == HDLC.ESC):
|
||||
escape = True
|
||||
else:
|
||||
if (escape):
|
||||
if (byte == HDLC.FLAG ^ HDLC.ESC_MASK):
|
||||
byte = HDLC.FLAG
|
||||
if (byte == HDLC.ESC ^ HDLC.ESC_MASK):
|
||||
byte = HDLC.ESC
|
||||
escape = False
|
||||
data_buffer = data_buffer+bytes([byte])
|
||||
else:
|
||||
self.online = False
|
||||
if self.initiator and not self.detached:
|
||||
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
|
||||
self.reconnect()
|
||||
else:
|
||||
RNS.log("Socket for remote client "+str(self)+" was closed.", RNS.LOG_VERBOSE)
|
||||
self.teardown()
|
||||
|
||||
break
|
||||
|
||||
|
||||
except Exception as e:
|
||||
self.online = False
|
||||
RNS.log("An interface error occurred for "+str(self)+", the contained exception was: "+str(e), RNS.LOG_WARNING)
|
||||
|
||||
if self.initiator:
|
||||
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
|
||||
self.reconnect()
|
||||
else:
|
||||
self.teardown()
|
||||
|
||||
def teardown(self):
|
||||
if self.initiator and not self.detached:
|
||||
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||
if RNS.Reticulum.panic_on_interface_error:
|
||||
RNS.panic()
|
||||
|
||||
else:
|
||||
RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE)
|
||||
|
||||
self.online = False
|
||||
self.OUT = False
|
||||
self.IN = False
|
||||
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||
self.parent_interface.clients -= 1
|
||||
|
||||
if self in RNS.Transport.interfaces:
|
||||
if not self.initiator:
|
||||
RNS.Transport.interfaces.remove(self)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "I2PInterfacePeer["+str(self.name)+"]"
|
||||
|
||||
|
||||
class I2PInterface(Interface):
|
||||
BITRATE_GUESS = 256*1000
|
||||
|
||||
def __init__(self, owner, name, rns_storagepath, peers, connectable = True):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
self.online = False
|
||||
self.clients = 0
|
||||
self.owner = owner
|
||||
self.connectable = connectable
|
||||
self.i2p_tunneled = True
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
|
||||
self.b32 = None
|
||||
self.i2p = I2PController(rns_storagepath)
|
||||
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
self.name = name
|
||||
|
||||
|
||||
self.receives = True
|
||||
self.bind_ip = "127.0.0.1"
|
||||
self.bind_port = self.i2p.get_free_port()
|
||||
self.address = (self.bind_ip, self.bind_port)
|
||||
self.bitrate = I2PInterface.BITRATE_GUESS
|
||||
|
||||
i2p_thread = threading.Thread(target=self.i2p.start)
|
||||
i2p_thread.setDaemon(True)
|
||||
i2p_thread.start()
|
||||
|
||||
def handlerFactory(callback):
|
||||
def createHandler(*args, **keys):
|
||||
return I2PInterfaceHandler(callback, *args, **keys)
|
||||
return createHandler
|
||||
|
||||
ThreadingI2PServer.allow_reuse_address = True
|
||||
self.server = ThreadingI2PServer(self.address, handlerFactory(self.incoming_connection))
|
||||
|
||||
thread = threading.Thread(target=self.server.serve_forever)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
if self.connectable:
|
||||
def tunnel_job():
|
||||
self.i2p.server_tunnel(self)
|
||||
|
||||
thread = threading.Thread(target=tunnel_job)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
if peers != None:
|
||||
for peer_addr in peers:
|
||||
interface_name = peer_addr
|
||||
peer_interface = I2PInterfacePeer(self, self.owner, interface_name, peer_addr)
|
||||
peer_interface.OUT = True
|
||||
peer_interface.IN = True
|
||||
peer_interface.parent_interface = self
|
||||
peer_interface.parent_count = False
|
||||
RNS.Transport.interfaces.append(peer_interface)
|
||||
|
||||
self.online = True
|
||||
|
||||
|
||||
def incoming_connection(self, handler):
|
||||
RNS.log("Accepting incoming I2P connection", RNS.LOG_VERBOSE)
|
||||
interface_name = "Connected peer on "+self.name
|
||||
spawned_interface = I2PInterfacePeer(self, self.owner, interface_name, connected_socket=handler.request)
|
||||
spawned_interface.OUT = True
|
||||
spawned_interface.IN = True
|
||||
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
|
||||
RNS.log("Spawned new I2PInterface Peer: "+str(spawned_interface), RNS.LOG_VERBOSE)
|
||||
RNS.Transport.interfaces.append(spawned_interface)
|
||||
self.clients += 1
|
||||
spawned_interface.read_loop()
|
||||
|
||||
def processOutgoing(self, data):
|
||||
pass
|
||||
|
||||
def detach(self):
|
||||
self.i2p.stop()
|
||||
|
||||
def __str__(self):
|
||||
return "I2PInterface["+self.name+"]"
|
||||
|
||||
class I2PInterfaceHandler(socketserver.BaseRequestHandler):
|
||||
def __init__(self, callback, *args, **keys):
|
||||
self.callback = callback
|
||||
socketserver.BaseRequestHandler.__init__(self, *args, **keys)
|
||||
|
||||
def handle(self):
|
||||
self.callback(handler=self)
|
||||
@@ -1,4 +1,28 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import time
|
||||
import threading
|
||||
|
||||
class Interface:
|
||||
IN = False
|
||||
@@ -7,6 +31,10 @@ class Interface:
|
||||
RPT = False
|
||||
name = None
|
||||
|
||||
MODE_FULL = 0x01
|
||||
MODE_POINT_TO_POINT = 0x02
|
||||
MODE_ACCESS_POINT = 0x03
|
||||
|
||||
def __init__(self):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
@@ -15,5 +43,42 @@ class Interface:
|
||||
def get_hash(self):
|
||||
return RNS.Identity.full_hash(str(self).encode("utf-8"))
|
||||
|
||||
def process_announce_queue(self):
|
||||
if not hasattr(self, "announce_cap"):
|
||||
self.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
|
||||
|
||||
if hasattr(self, "announce_queue"):
|
||||
try:
|
||||
now = time.time()
|
||||
stale = []
|
||||
for a in self.announce_queue:
|
||||
if now > a["time"]+RNS.Reticulum.QUEUED_ANNOUNCE_LIFE:
|
||||
stale.append(a)
|
||||
|
||||
for s in stale:
|
||||
self.announce_queue.remove(s)
|
||||
|
||||
if len(self.announce_queue) > 0:
|
||||
min_hops = min(entry["hops"] for entry in self.announce_queue)
|
||||
entries = list(filter(lambda e: e["hops"] == min_hops, self.announce_queue))
|
||||
entries.sort(key=lambda e: e["time"])
|
||||
selected = entries[0]
|
||||
|
||||
now = time.time()
|
||||
tx_time = (len(selected["raw"])*8) / self.bitrate
|
||||
wait_time = (tx_time / self.announce_cap)
|
||||
self.announce_allowed_at = now + wait_time
|
||||
|
||||
self.processOutgoing(selected["raw"])
|
||||
self.announce_queue.remove(selected)
|
||||
if len(self.announce_queue) > 0:
|
||||
timer = threading.Timer(wait_time, self.process_announce_queue)
|
||||
timer.start()
|
||||
|
||||
except Exception as e:
|
||||
self.announce_queue = []
|
||||
RNS.log("Error while processing announce queue on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log("The announce queue for this interface has been cleared.", RNS.LOG_ERROR)
|
||||
|
||||
def detach(self):
|
||||
pass
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
from time import sleep
|
||||
import sys
|
||||
@@ -29,6 +51,7 @@ class KISS():
|
||||
|
||||
class KISSInterface(Interface):
|
||||
MAX_CHUNK = 32768
|
||||
BITRATE_GUESS = 1200
|
||||
|
||||
owner = None
|
||||
port = None
|
||||
@@ -67,6 +90,7 @@ class KISSInterface(Interface):
|
||||
self.beacon_i = beacon_interval
|
||||
self.beacon_d = beacon_data.encode("utf-8")
|
||||
self.first_tx = None
|
||||
self.bitrate = KISSInterface.BITRATE_GUESS
|
||||
|
||||
self.packet_queue = []
|
||||
self.flow_control = flow_control
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
import socketserver
|
||||
import threading
|
||||
@@ -37,6 +59,7 @@ class LocalClientInterface(Interface):
|
||||
self.never_connected = True
|
||||
self.detached = False
|
||||
self.name = name
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
|
||||
if connected_socket != None:
|
||||
self.receives = True
|
||||
@@ -53,6 +76,7 @@ class LocalClientInterface(Interface):
|
||||
self.connect()
|
||||
|
||||
self.owner = owner
|
||||
self.bitrate = 1000*1000*1000
|
||||
self.online = True
|
||||
self.writing = False
|
||||
|
||||
@@ -237,6 +261,7 @@ class LocalServerInterface(Interface):
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
self.name = "Reticulum"
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
|
||||
if (bindport != None):
|
||||
self.receives = True
|
||||
@@ -260,6 +285,7 @@ class LocalServerInterface(Interface):
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
self.bitrate = 1000*1000*1000
|
||||
self.online = True
|
||||
|
||||
|
||||
@@ -272,7 +298,8 @@ class LocalServerInterface(Interface):
|
||||
spawned_interface.target_ip = handler.client_address[0]
|
||||
spawned_interface.target_port = str(handler.client_address[1])
|
||||
spawned_interface.parent_interface = self
|
||||
RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_VERBOSE)
|
||||
spawned_interface.bitrate = self.bitrate
|
||||
RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_EXTREME)
|
||||
RNS.Transport.interfaces.append(spawned_interface)
|
||||
RNS.Transport.local_client_interfaces.append(spawned_interface)
|
||||
self.clients += 1
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
from time import sleep
|
||||
@@ -30,8 +51,11 @@ class KISS():
|
||||
CMD_STAT_SNR = 0x24
|
||||
CMD_BLINK = 0x30
|
||||
CMD_RANDOM = 0x40
|
||||
CMD_PLATFORM = 0x48
|
||||
CMD_MCU = 0x49
|
||||
CMD_FW_VERSION = 0x50
|
||||
CMD_ROM_READ = 0x51
|
||||
CMD_RESET = 0x55
|
||||
|
||||
DETECT_REQ = 0x73
|
||||
DETECT_RESP = 0x46
|
||||
@@ -45,6 +69,9 @@ class KISS():
|
||||
ERROR_TXFAILED = 0x02
|
||||
ERROR_EEPROM_LOCKED = 0x03
|
||||
|
||||
PLATFORM_AVR = 0x90
|
||||
PLATFORM_ESP32 = 0x80
|
||||
|
||||
@staticmethod
|
||||
def escape(data):
|
||||
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
|
||||
@@ -70,6 +97,9 @@ class RNodeInterface(Interface):
|
||||
|
||||
CALLSIGN_MAX_LEN = 32
|
||||
|
||||
REQUIRED_FW_VER_MAJ = 1
|
||||
REQUIRED_FW_VER_MIN = 26
|
||||
|
||||
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None):
|
||||
import importlib
|
||||
if importlib.util.find_spec('serial') != None:
|
||||
@@ -101,6 +131,12 @@ class RNodeInterface(Interface):
|
||||
self.cr = cr
|
||||
self.state = KISS.RADIO_STATE_OFF
|
||||
self.bitrate = 0
|
||||
self.platform = None
|
||||
self.mcu = None
|
||||
self.detected = False
|
||||
self.firmware_ok = False
|
||||
self.maj_version = 0
|
||||
self.min_version = 0
|
||||
|
||||
self.last_id = 0
|
||||
self.first_tx = None
|
||||
@@ -190,6 +226,17 @@ class RNodeInterface(Interface):
|
||||
thread = threading.Thread(target=self.readLoop)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
self.detect()
|
||||
sleep(0.1)
|
||||
|
||||
if not self.detected:
|
||||
raise IOError("Could not detect device")
|
||||
else:
|
||||
if self.platform == KISS.PLATFORM_ESP32:
|
||||
RNS.log("Resetting ESP32-based device before configuration...", RNS.LOG_VERBOSE)
|
||||
self.hard_reset()
|
||||
|
||||
self.online = True
|
||||
RNS.log("Serial port "+self.port+" is now open")
|
||||
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
|
||||
@@ -203,7 +250,7 @@ class RNodeInterface(Interface):
|
||||
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
||||
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
||||
self.serial.close()
|
||||
raise IOError("RNode interface did not pass validation")
|
||||
raise IOError("RNode interface did not pass configuration validation")
|
||||
|
||||
|
||||
def initRadio(self):
|
||||
@@ -214,6 +261,19 @@ class RNodeInterface(Interface):
|
||||
self.setCodingRate()
|
||||
self.setRadioState(KISS.RADIO_STATE_ON)
|
||||
|
||||
def detect(self):
|
||||
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND])
|
||||
written = self.serial.write(kiss_command)
|
||||
if written != len(kiss_command):
|
||||
raise IOError("An IO error occurred while detecting hardware for "+self(str))
|
||||
|
||||
def hard_reset(self):
|
||||
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
|
||||
written = self.serial.write(kiss_command)
|
||||
if written != len(kiss_command):
|
||||
raise IOError("An IO error occurred while restarting device")
|
||||
sleep(2.25);
|
||||
|
||||
def setFrequency(self):
|
||||
c1 = self.frequency >> 24
|
||||
c2 = self.frequency >> 16 & 0xFF
|
||||
@@ -260,13 +320,28 @@ class RNodeInterface(Interface):
|
||||
raise IOError("An IO error occurred while configuring coding rate for "+self(str))
|
||||
|
||||
def setRadioState(self, state):
|
||||
self.state = state
|
||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
|
||||
written = self.serial.write(kiss_command)
|
||||
if written != len(kiss_command):
|
||||
raise IOError("An IO error occurred while configuring radio state for "+self(str))
|
||||
|
||||
def validate_firmware(self):
|
||||
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
|
||||
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
|
||||
self.firmware_ok = True
|
||||
|
||||
if self.firmware_ok:
|
||||
return
|
||||
|
||||
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR)
|
||||
RNS.log("This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR)
|
||||
RNS.log("Please update your RNode firmware with rnodeconf (https://github.com/markqvist/rnodeconfigutil/)")
|
||||
RNS.panic()
|
||||
|
||||
|
||||
def validateRadioState(self):
|
||||
RNS.log("Validating radio configuration for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
RNS.log("Wating for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
sleep(0.25);
|
||||
if (self.frequency != self.r_frequency):
|
||||
RNS.log("Frequency mismatch", RNS.LOG_ERROR)
|
||||
@@ -280,6 +355,9 @@ class RNodeInterface(Interface):
|
||||
if (self.sf != self.r_sf):
|
||||
RNS.log("Spreading factor mismatch", RNS.LOG_ERROR)
|
||||
self.validcfg = False
|
||||
if (self.state != self.r_state):
|
||||
RNS.log("Radio state mismatch", RNS.LOG_ERROR)
|
||||
self.validcfg = False
|
||||
|
||||
if (self.validcfg):
|
||||
return True
|
||||
@@ -420,8 +498,30 @@ class RNodeInterface(Interface):
|
||||
self.updateBitrate()
|
||||
elif (command == KISS.CMD_RADIO_STATE):
|
||||
self.r_state = byte
|
||||
if self.r_state:
|
||||
pass
|
||||
#RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log(str(self)+" Radio reporting state is offline", RNS.LOG_DEBUG)
|
||||
|
||||
elif (command == KISS.CMD_RADIO_LOCK):
|
||||
self.r_lock = byte
|
||||
elif (command == KISS.CMD_FW_VERSION):
|
||||
if (byte == KISS.FESC):
|
||||
escape = True
|
||||
else:
|
||||
if (escape):
|
||||
if (byte == KISS.TFEND):
|
||||
byte = KISS.FEND
|
||||
if (byte == KISS.TFESC):
|
||||
byte = KISS.FESC
|
||||
escape = False
|
||||
command_buffer = command_buffer+bytes([byte])
|
||||
if (len(command_buffer) == 2):
|
||||
self.maj_version = int(command_buffer[0])
|
||||
self.min_version = int(command_buffer[1])
|
||||
self.validate_firmware()
|
||||
|
||||
elif (command == KISS.CMD_STAT_RX):
|
||||
if (byte == KISS.FESC):
|
||||
escape = True
|
||||
@@ -456,15 +556,33 @@ class RNodeInterface(Interface):
|
||||
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
|
||||
elif (command == KISS.CMD_RANDOM):
|
||||
self.r_random = byte
|
||||
elif (command == KISS.CMD_PLATFORM):
|
||||
self.platform = byte
|
||||
elif (command == KISS.CMD_MCU):
|
||||
self.mcu = byte
|
||||
elif (command == KISS.CMD_ERROR):
|
||||
if (byte == KISS.ERROR_INITRADIO):
|
||||
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
|
||||
raise IOError("Radio initialisation failure")
|
||||
elif (byte == KISS.ERROR_INITRADIO):
|
||||
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
|
||||
raise IOError("Hardware transmit failure")
|
||||
else:
|
||||
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
|
||||
raise IOError("Unknown hardware failure")
|
||||
elif (command == KISS.CMD_RESET):
|
||||
if (byte == 0xF8):
|
||||
if self.platform == KISS.PLATFORM_ESP32:
|
||||
if self.online:
|
||||
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
|
||||
raise IOError("ESP32 reset")
|
||||
elif (command == KISS.CMD_READY):
|
||||
self.process_queue()
|
||||
elif (command == KISS.CMD_DETECT):
|
||||
if byte == KISS.DETECT_RESP:
|
||||
self.detected = True
|
||||
else:
|
||||
self.detected = False
|
||||
|
||||
else:
|
||||
time_since_last = int(time.time()*1000) - last_read_ms
|
||||
@@ -487,7 +605,7 @@ class RNodeInterface(Interface):
|
||||
self.online = False
|
||||
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||
|
||||
|
||||
if RNS.Reticulum.panic_on_interface_error:
|
||||
RNS.panic()
|
||||
|
||||
@@ -500,7 +618,7 @@ class RNodeInterface(Interface):
|
||||
def reconnect_port(self):
|
||||
while not self.online:
|
||||
try:
|
||||
time.sleep(5)
|
||||
time.sleep(3.5)
|
||||
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
self.open_port()
|
||||
if self.serial.is_open:
|
||||
@@ -511,5 +629,5 @@ class RNodeInterface(Interface):
|
||||
RNS.log("Reconnected serial port for "+str(self))
|
||||
|
||||
def __str__(self):
|
||||
return "RNodeInterface["+self.name+"]"
|
||||
return "RNodeInterface["+str(self.name)+"]"
|
||||
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
from time import sleep
|
||||
import sys
|
||||
@@ -52,6 +74,7 @@ class SerialInterface(Interface):
|
||||
self.stopbits = stopbits
|
||||
self.timeout = 100
|
||||
self.online = False
|
||||
self.bitrate = self.speed
|
||||
|
||||
if parity.lower() == "e" or parity.lower() == "even":
|
||||
self.parity = serial.PARITY_EVEN
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
import socketserver
|
||||
import threading
|
||||
@@ -37,6 +59,8 @@ class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
class TCPClientInterface(Interface):
|
||||
BITRATE_GUESS = 10*1000*1000
|
||||
|
||||
RECONNECT_WAIT = 5
|
||||
RECONNECT_MAX_TRIES = None
|
||||
|
||||
@@ -46,7 +70,12 @@ class TCPClientInterface(Interface):
|
||||
TCP_PROBE_INTERVAL = 3
|
||||
TCP_PROBES = 5
|
||||
|
||||
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False):
|
||||
I2P_USER_TIMEOUT = 40
|
||||
I2P_PROBE_AFTER = 10
|
||||
I2P_PROBE_INTERVAL = 5
|
||||
I2P_PROBES = 6
|
||||
|
||||
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
@@ -63,7 +92,10 @@ class TCPClientInterface(Interface):
|
||||
self.online = False
|
||||
self.detached = False
|
||||
self.kiss_framing = kiss_framing
|
||||
|
||||
self.i2p_tunneled = i2p_tunneled
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
self.bitrate = TCPClientInterface.BITRATE_GUESS
|
||||
|
||||
if max_reconnect_tries == None:
|
||||
self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES
|
||||
else:
|
||||
@@ -99,12 +131,18 @@ class TCPClientInterface(Interface):
|
||||
|
||||
|
||||
def set_timeouts_linux(self):
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(TCPClientInterface.TCP_USER_TIMEOUT * 1000))
|
||||
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(TCPClientInterface.TCP_PROBE_INTERVAL))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(TCPClientInterface.TCP_PROBES))
|
||||
if not self.i2p_tunneled:
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(TCPClientInterface.TCP_USER_TIMEOUT * 1000))
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(TCPClientInterface.TCP_PROBE_INTERVAL))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(TCPClientInterface.TCP_PROBES))
|
||||
else:
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(TCPClientInterface.I2P_USER_TIMEOUT * 1000))
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(TCPClientInterface.I2P_PROBE_INTERVAL))
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(TCPClientInterface.I2P_PROBES))
|
||||
|
||||
def set_timeouts_osx(self):
|
||||
if hasattr(socket, "TCP_KEEPALIVE"):
|
||||
@@ -112,9 +150,13 @@ class TCPClientInterface(Interface):
|
||||
else:
|
||||
TCP_KEEPIDLE = 0x10
|
||||
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
|
||||
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
|
||||
if not self.i2p_tunneled:
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
|
||||
else:
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
|
||||
|
||||
def detach(self):
|
||||
if self.socket != None:
|
||||
if hasattr(self.socket, "close"):
|
||||
@@ -336,6 +378,8 @@ class TCPClientInterface(Interface):
|
||||
|
||||
|
||||
class TCPServerInterface(Interface):
|
||||
BITRATE_GUESS = 10*1000*1000
|
||||
|
||||
@staticmethod
|
||||
def get_address_for_if(name):
|
||||
import importlib
|
||||
@@ -358,7 +402,7 @@ class TCPServerInterface(Interface):
|
||||
RNS.log("You can install it with the command: python3 -m pip install netifaces", RNS.LOG_CRITICAL)
|
||||
RNS.panic()
|
||||
|
||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None):
|
||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
self.online = False
|
||||
@@ -368,6 +412,9 @@ class TCPServerInterface(Interface):
|
||||
self.OUT = False
|
||||
self.name = name
|
||||
|
||||
self.i2p_tunneled = i2p_tunneled
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
|
||||
if device != None:
|
||||
bindip = TCPServerInterface.get_address_for_if(device)
|
||||
|
||||
@@ -387,6 +434,8 @@ class TCPServerInterface(Interface):
|
||||
ThreadingTCPServer.allow_reuse_address = True
|
||||
self.server = ThreadingTCPServer(address, handlerFactory(self.incoming_connection))
|
||||
|
||||
self.bitrate = TCPServerInterface.BITRATE_GUESS
|
||||
|
||||
thread = threading.Thread(target=self.server.serve_forever)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
@@ -397,12 +446,16 @@ class TCPServerInterface(Interface):
|
||||
def incoming_connection(self, handler):
|
||||
RNS.log("Accepting incoming TCP connection", RNS.LOG_VERBOSE)
|
||||
interface_name = "Client on "+self.name
|
||||
spawned_interface = TCPClientInterface(self.owner, interface_name, target_ip=None, target_port=None, connected_socket=handler.request)
|
||||
spawned_interface = TCPClientInterface(self.owner, interface_name, target_ip=None, target_port=None, connected_socket=handler.request, i2p_tunneled=self.i2p_tunneled)
|
||||
spawned_interface.OUT = self.OUT
|
||||
spawned_interface.IN = self.IN
|
||||
spawned_interface.target_ip = handler.client_address[0]
|
||||
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
|
||||
spawned_interface.online = True
|
||||
RNS.log("Spawned new TCPClient Interface: "+str(spawned_interface), RNS.LOG_VERBOSE)
|
||||
RNS.Transport.interfaces.append(spawned_interface)
|
||||
@@ -421,4 +474,4 @@ class TCPInterfaceHandler(socketserver.BaseRequestHandler):
|
||||
socketserver.BaseRequestHandler.__init__(self, *args, **keys)
|
||||
|
||||
def handle(self):
|
||||
self.callback(handler=self)
|
||||
self.callback(handler=self)
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .Interface import Interface
|
||||
import socketserver
|
||||
import threading
|
||||
@@ -8,6 +30,7 @@ import RNS
|
||||
|
||||
|
||||
class UDPInterface(Interface):
|
||||
BITRATE_GUESS = 10*1000*1000
|
||||
|
||||
@staticmethod
|
||||
def get_address_for_if(name):
|
||||
@@ -38,6 +61,7 @@ class UDPInterface(Interface):
|
||||
self.OUT = False
|
||||
self.name = name
|
||||
self.online = False
|
||||
self.bitrate = UDPInterface.BITRATE_GUESS
|
||||
|
||||
if device != None:
|
||||
if bindip == None:
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
|
||||
+32
-4
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
@@ -15,6 +37,8 @@ import RNS
|
||||
|
||||
import traceback
|
||||
|
||||
cio_default_backend = default_backend()
|
||||
|
||||
class LinkCallbacks:
|
||||
def __init__(self):
|
||||
self.link_established = None
|
||||
@@ -43,7 +67,7 @@ class Link:
|
||||
ECPUBSIZE = 32+32
|
||||
KEYSIZE = 32
|
||||
|
||||
MDU = math.floor((RNS.Reticulum.MTU-RNS.Reticulum.HEADER_MINSIZE-RNS.Identity.FERNET_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
|
||||
MDU = math.floor((RNS.Reticulum.MTU-RNS.Reticulum.IFAC_MIN_SIZE-RNS.Reticulum.HEADER_MINSIZE-RNS.Identity.OPTIMISED_FERNET_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
|
||||
|
||||
ESTABLISHMENT_TIMEOUT_PER_HOP = RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||
"""
|
||||
@@ -199,11 +223,14 @@ class Link:
|
||||
def handshake(self):
|
||||
self.status = Link.HANDSHAKE
|
||||
self.shared_key = self.prv.exchange(self.peer_pub)
|
||||
|
||||
# TODO: Improve this re-allocation of HKDF
|
||||
self.derived_key = HKDF(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=self.get_salt(),
|
||||
info=self.get_context(),
|
||||
backend=cio_default_backend,
|
||||
).derive(self.shared_key)
|
||||
|
||||
def prove(self):
|
||||
@@ -551,7 +578,8 @@ class Link:
|
||||
break
|
||||
|
||||
if remove != None:
|
||||
self.pending_requests.remove(remove)
|
||||
if remove in self.pending_requests:
|
||||
self.pending_requests.remove(remove)
|
||||
|
||||
def request_resource_concluded(self, resource):
|
||||
if resource.status == RNS.Resource.COMPLETE:
|
||||
@@ -765,7 +793,7 @@ class Link:
|
||||
return plaintext
|
||||
except Exception as e:
|
||||
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log(traceback.format_exc(), RNS.LOG_ERROR)
|
||||
# RNS.log(traceback.format_exc(), RNS.LOG_ERROR)
|
||||
# TODO: Think long about implications here
|
||||
# self.teardown()
|
||||
|
||||
@@ -1063,4 +1091,4 @@ class RequestReceiptCallbacks:
|
||||
def __init__(self):
|
||||
self.response = None
|
||||
self.failed = None
|
||||
self.progress = None
|
||||
self.progress = None
|
||||
|
||||
+47
-19
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import threading
|
||||
import struct
|
||||
import math
|
||||
@@ -72,7 +94,7 @@ class Packet:
|
||||
"""
|
||||
PLAIN_MDU = MDU
|
||||
"""
|
||||
The maximum size of the payload data in a single unencrypted packet
|
||||
The maximum size of the payload data in a single unencrypted packet
|
||||
"""
|
||||
|
||||
TIMEOUT_PER_HOP = RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||
@@ -185,27 +207,33 @@ class Packet:
|
||||
|
||||
|
||||
def unpack(self):
|
||||
self.flags = self.raw[0]
|
||||
self.hops = self.raw[1]
|
||||
try:
|
||||
self.flags = self.raw[0]
|
||||
self.hops = self.raw[1]
|
||||
|
||||
self.header_type = (self.flags & 0b11000000) >> 6
|
||||
self.transport_type = (self.flags & 0b00110000) >> 4
|
||||
self.destination_type = (self.flags & 0b00001100) >> 2
|
||||
self.packet_type = (self.flags & 0b00000011)
|
||||
self.header_type = (self.flags & 0b11000000) >> 6
|
||||
self.transport_type = (self.flags & 0b00110000) >> 4
|
||||
self.destination_type = (self.flags & 0b00001100) >> 2
|
||||
self.packet_type = (self.flags & 0b00000011)
|
||||
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
self.transport_id = self.raw[2:12]
|
||||
self.destination_hash = self.raw[12:22]
|
||||
self.context = ord(self.raw[22:23])
|
||||
self.data = self.raw[23:]
|
||||
else:
|
||||
self.transport_id = None
|
||||
self.destination_hash = self.raw[2:12]
|
||||
self.context = ord(self.raw[12:13])
|
||||
self.data = self.raw[13:]
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
self.transport_id = self.raw[2:12]
|
||||
self.destination_hash = self.raw[12:22]
|
||||
self.context = ord(self.raw[22:23])
|
||||
self.data = self.raw[23:]
|
||||
else:
|
||||
self.transport_id = None
|
||||
self.destination_hash = self.raw[2:12]
|
||||
self.context = ord(self.raw[12:13])
|
||||
self.data = self.raw[13:]
|
||||
|
||||
self.packed = False
|
||||
self.update_hash()
|
||||
self.packed = False
|
||||
self.update_hash()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Received malformed packet, dropping it. The contained exception was: "+str(e), RNS.LOG_EXTREME)
|
||||
return False
|
||||
|
||||
def send(self):
|
||||
"""
|
||||
|
||||
+26
-1
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import os
|
||||
import bz2
|
||||
@@ -143,6 +165,8 @@ class Resource:
|
||||
def __init__(self, data, link, advertise=True, auto_compress=True, callback=None, progress_callback=None, timeout = None, segment_index = 1, original_hash = None, request_id = None, is_response = False):
|
||||
data_size = None
|
||||
resource_data = None
|
||||
self.assembly_lock = False
|
||||
|
||||
if hasattr(data, "read"):
|
||||
data_size = os.stat(data.name).st_size
|
||||
self.total_size = data_size
|
||||
@@ -601,7 +625,8 @@ class Resource:
|
||||
|
||||
self.receiving_part = False
|
||||
|
||||
if self.received_count == self.total_parts:
|
||||
if self.received_count == self.total_parts and not self.assembly_lock:
|
||||
self.assembly_lock = True
|
||||
self.assemble()
|
||||
elif self.outstanding_parts == 0:
|
||||
# TODO: Figure out if there is a mathematically
|
||||
|
||||
+410
-322
@@ -1,8 +1,39 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .vendor.platformutils import get_platform
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
cio_default_backend = default_backend()
|
||||
|
||||
if get_platform() == "android":
|
||||
# TODO: Selectively import Android-relevant interfaces
|
||||
pass
|
||||
from .Interfaces import Interface
|
||||
from .Interfaces import LocalInterface
|
||||
from .Interfaces import AutoInterface
|
||||
from .Interfaces import TCPInterface
|
||||
from .Interfaces import UDPInterface
|
||||
from .Interfaces import I2PInterface
|
||||
else:
|
||||
from .Interfaces import *
|
||||
|
||||
@@ -45,12 +76,12 @@ class Reticulum:
|
||||
other programs to use on demand.
|
||||
"""
|
||||
|
||||
# Future minimum will probably be locked in at 244 bytes to support
|
||||
# networks with segments of different MTUs. Absolute minimum is 211.
|
||||
# Future minimum will probably be locked in at 251 bytes to support
|
||||
# networks with segments of different MTUs. Absolute minimum is 219.
|
||||
MTU = 500
|
||||
"""
|
||||
The MTU that Reticulum adheres to, and will expect other peers to
|
||||
adhere to. By default, the MTU is 500 bytes. In custom RNS network
|
||||
adhere to. By default, the MTU is 507 bytes. In custom RNS network
|
||||
implementations, it is possible to change this value, but doing so will
|
||||
completely break compatibility with all other RNS networks. An identical
|
||||
MTU is a prerequisite for peers to communicate in the same network.
|
||||
@@ -59,6 +90,29 @@ class Reticulum:
|
||||
the default value.
|
||||
"""
|
||||
|
||||
MAX_QUEUED_ANNOUNCES = 16384
|
||||
QUEUED_ANNOUNCE_LIFE = 60*60*24
|
||||
|
||||
ANNOUNCE_CAP = 2
|
||||
"""
|
||||
The maximum percentage of interface bandwidth that, at any given time,
|
||||
may be used to propagate announces. If an announce was scheduled for
|
||||
broadcasting on an interface, but doing so would exceed the allowed
|
||||
bandwidth allocation, the announce will be queued for transmission
|
||||
when there is bandwidth available.
|
||||
|
||||
Reticulum will always prioritise propagating announces with fewer
|
||||
hops, ensuring that distant, large networks with many peers on fast
|
||||
links don't overwhelm the capacity of smaller networks on slower
|
||||
mediums. If an announce remains queued for an extended amount of time,
|
||||
it will eventually be dropped.
|
||||
|
||||
This value will be applied by default to all created interfaces,
|
||||
but it can be configured individually on a per-interface basis.
|
||||
"""
|
||||
|
||||
MINIMUM_BITRATE = 500
|
||||
|
||||
# TODO: To reach the 300bps level without unreasonably impacting
|
||||
# performance on faster links, we need a mechanism for setting
|
||||
# this value more intelligently. One option could be inferring it
|
||||
@@ -72,8 +126,10 @@ class Reticulum:
|
||||
|
||||
HEADER_MINSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*1
|
||||
HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
|
||||
IFAC_MIN_SIZE = 1
|
||||
IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
|
||||
|
||||
MDU = MTU - HEADER_MAXSIZE
|
||||
MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
|
||||
|
||||
router = None
|
||||
config = None
|
||||
@@ -116,6 +172,8 @@ class Reticulum:
|
||||
:param configdir: Full path to a Reticulum configuration directory.
|
||||
"""
|
||||
|
||||
RNS.vendor.platformutils.platform_checks()
|
||||
|
||||
if configdir != None:
|
||||
Reticulum.configdir = configdir
|
||||
|
||||
@@ -134,6 +192,8 @@ class Reticulum:
|
||||
self.share_instance = True
|
||||
self.rpc_listener = None
|
||||
|
||||
self.ifac_salt = Reticulum.IFAC_SALT
|
||||
|
||||
self.requested_loglevel = loglevel
|
||||
if self.requested_loglevel != None:
|
||||
if self.requested_loglevel > RNS.LOG_EXTREME:
|
||||
@@ -268,39 +328,110 @@ class Reticulum:
|
||||
self.__start_local_interface()
|
||||
|
||||
if self.is_shared_instance or self.is_standalone_instance:
|
||||
RNS.log("Bringing up system interfaces...", RNS.LOG_DEBUG)
|
||||
interface_names = []
|
||||
for name in self.config["interfaces"]:
|
||||
if not name in interface_names:
|
||||
# TODO: We really need to generalise this way of instantiating
|
||||
# and configuring interfaces. Ideally, interfaces should just
|
||||
# have a conrfig dict passed to their init method, and return
|
||||
# a ready interface, onto which this routine can configure any
|
||||
# generic or extra parameters.
|
||||
|
||||
c = self.config["interfaces"][name]
|
||||
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
if "interface_mode" in c:
|
||||
if c["interface_mode"] == "full":
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
elif c["interface_mode"] == "access_point" or c["interface_mode"] == "accesspoint" or c["interface_mode"] == "ap":
|
||||
interface_mode = Interface.Interface.MODE_ACCESS_POINT
|
||||
elif c["interface_mode"] == "pointtopoint" or c["interface_mode"] == "ptp":
|
||||
interface_mode = Interface.Interface.MODE_POINT_TO_POINT
|
||||
|
||||
elif "mode" in c:
|
||||
if c["mode"] == "full":
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
elif c["mode"] == "access_point" or c["mode"] == "accesspoint" or c["mode"] == "ap":
|
||||
interface_mode = Interface.Interface.MODE_ACCESS_POINT
|
||||
elif c["mode"] == "pointtopoint" or c["mode"] == "ptp":
|
||||
interface_mode = Interface.Interface.MODE_POINT_TO_POINT
|
||||
|
||||
ifac_size = None
|
||||
if "ifac_size" in c:
|
||||
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
|
||||
ifac_size = c.as_int("ifac_size")//8
|
||||
|
||||
ifac_netname = None
|
||||
if "networkname" in c:
|
||||
if c["networkname"] != "":
|
||||
ifac_netname = c["networkname"]
|
||||
if "network_name" in c:
|
||||
if c["network_name"] != "":
|
||||
ifac_netname = c["network_name"]
|
||||
|
||||
ifac_netkey = None
|
||||
if "passphrase" in c:
|
||||
if c["passphrase"] != "":
|
||||
ifac_netkey = c["passphrase"]
|
||||
if "pass_phrase" in c:
|
||||
if c["pass_phrase"] != "":
|
||||
ifac_netkey = c["pass_phrase"]
|
||||
|
||||
configured_bitrate = None
|
||||
if "bitrate" in c:
|
||||
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
|
||||
configured_bitrate = c.as_int("bitrate")
|
||||
|
||||
announce_cap = Reticulum.ANNOUNCE_CAP/100.0
|
||||
if "announce_cap" in c:
|
||||
if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
|
||||
announce_cap = c.as_float("announce_cap")/100.0
|
||||
|
||||
try:
|
||||
if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True:
|
||||
interface = None
|
||||
|
||||
if (("interface_enabled" in c) and c.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True):
|
||||
if c["type"] == "AutoInterface":
|
||||
group_id = c["group_id"] if "group_id" in c else None
|
||||
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
|
||||
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
|
||||
data_port = int(c["data_port"]) if "data_port" in c else None
|
||||
allowed_interfaces = c.as_list("devices") if "devices" in c else None
|
||||
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
|
||||
if not RNS.vendor.platformutils.is_windows():
|
||||
group_id = c["group_id"] if "group_id" in c else None
|
||||
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
|
||||
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
|
||||
data_port = int(c["data_port"]) if "data_port" in c else None
|
||||
allowed_interfaces = c.as_list("devices") if "devices" in c else None
|
||||
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
|
||||
|
||||
interface = AutoInterface.AutoInterface(
|
||||
RNS.Transport,
|
||||
name,
|
||||
group_id,
|
||||
discovery_scope,
|
||||
discovery_port,
|
||||
data_port,
|
||||
allowed_interfaces,
|
||||
ignored_interfaces
|
||||
)
|
||||
interface = AutoInterface.AutoInterface(
|
||||
RNS.Transport,
|
||||
name,
|
||||
group_id,
|
||||
discovery_scope,
|
||||
discovery_port,
|
||||
data_port,
|
||||
allowed_interfaces,
|
||||
ignored_interfaces
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
interface.mode = interface_mode
|
||||
|
||||
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 "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
interface.OUT = False
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
|
||||
RNS.log("AutoInterface is not currently supported on Windows, disabling interface.", RNS.LOG_ERROR);
|
||||
RNS.log("Please remove this AutoInterface instance from your configuration file.", RNS.LOG_ERROR);
|
||||
RNS.log("You will have to manually configure other interfaces for connectivity.", RNS.LOG_ERROR);
|
||||
|
||||
if c["type"] == "UDPInterface":
|
||||
device = c["device"] if "device" in c else None
|
||||
@@ -326,19 +457,27 @@ class Reticulum:
|
||||
forward_port
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
interface.mode = interface_mode
|
||||
|
||||
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"] == "TCPServerInterface":
|
||||
device = c["device"] if "device" in c else None
|
||||
port = int(c["port"]) if "port" in c else None
|
||||
listen_ip = c["listen_ip"] if "listen_ip" in c else None
|
||||
listen_port = int(c["listen_port"]) if "listen_port" in c else None
|
||||
i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False
|
||||
|
||||
if port != None:
|
||||
listen_port = port
|
||||
@@ -348,37 +487,94 @@ class Reticulum:
|
||||
name,
|
||||
device,
|
||||
listen_ip,
|
||||
listen_port
|
||||
listen_port,
|
||||
i2p_tunneled
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
if interface_mode != Interface.Interface.MODE_FULL:
|
||||
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
interface.mode = interface_mode
|
||||
|
||||
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"] == "TCPClientInterface":
|
||||
kiss_framing = False
|
||||
if "kiss_framing" in c and c.as_bool("kiss_framing") == True:
|
||||
kiss_framing = True
|
||||
i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False
|
||||
|
||||
|
||||
interface = TCPInterface.TCPClientInterface(
|
||||
RNS.Transport,
|
||||
name,
|
||||
c["target_host"],
|
||||
int(c["target_port"]),
|
||||
kiss_framing = kiss_framing
|
||||
kiss_framing = kiss_framing,
|
||||
i2p_tunneled = i2p_tunneled
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
if interface_mode != Interface.Interface.MODE_FULL:
|
||||
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
interface.mode = interface_mode
|
||||
|
||||
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"] == "I2PInterface":
|
||||
i2p_peers = c.as_list("peers") if "peers" in c else None
|
||||
connectable = c.as_bool("connectable") if "connectable" in c else False
|
||||
|
||||
interface = I2PInterface.I2PInterface(
|
||||
RNS.Transport,
|
||||
name,
|
||||
Reticulum.storagepath,
|
||||
i2p_peers,
|
||||
connectable = connectable,
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
if interface_mode != Interface.Interface.MODE_FULL:
|
||||
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
interface.mode = interface_mode
|
||||
|
||||
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
|
||||
@@ -400,12 +596,20 @@ class Reticulum:
|
||||
stopbits
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
interface.mode = interface_mode
|
||||
|
||||
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 = 8
|
||||
|
||||
if c["type"] == "KISSInterface":
|
||||
preamble = int(c["preamble"]) if "preamble" in c else None
|
||||
@@ -441,12 +645,20 @@ class Reticulum:
|
||||
beacon_data
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
interface.mode = interface_mode
|
||||
|
||||
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 = 8
|
||||
|
||||
if c["type"] == "AX25KISSInterface":
|
||||
preamble = int(c["preamble"]) if "preamble" in c else None
|
||||
@@ -483,12 +695,20 @@ class Reticulum:
|
||||
flow_control
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
interface.mode = interface_mode
|
||||
|
||||
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 = 8
|
||||
|
||||
if c["type"] == "RNodeInterface":
|
||||
frequency = int(c["frequency"]) if "frequency" in c else None
|
||||
@@ -519,12 +739,48 @@ class Reticulum:
|
||||
id_callsign = id_callsign
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == True:
|
||||
interface.OUT = True
|
||||
else:
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
interface.mode = interface_mode
|
||||
|
||||
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 = 8
|
||||
|
||||
if interface != None:
|
||||
interface.ifac_netname = ifac_netname
|
||||
interface.ifac_netkey = ifac_netkey
|
||||
|
||||
if interface.ifac_netname != None or interface.ifac_netkey != None:
|
||||
ifac_origin = b""
|
||||
|
||||
if interface.ifac_netname != None:
|
||||
ifac_origin += RNS.Identity.full_hash(interface.ifac_netname.encode("utf-8"))
|
||||
|
||||
if interface.ifac_netkey != None:
|
||||
ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
|
||||
|
||||
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
|
||||
interface.ifac_key = HKDF(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=64,
|
||||
salt=self.ifac_salt,
|
||||
info=None,
|
||||
backend=cio_default_backend,
|
||||
).derive(ifac_origin_hash)
|
||||
|
||||
interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
|
||||
interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
|
||||
|
||||
RNS.Transport.interfaces.append(interface)
|
||||
|
||||
else:
|
||||
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_DEBUG)
|
||||
|
||||
@@ -535,6 +791,9 @@ class Reticulum:
|
||||
else:
|
||||
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
|
||||
RNS.panic()
|
||||
|
||||
RNS.log("System interfaces are ready", RNS.LOG_DEBUG)
|
||||
|
||||
|
||||
|
||||
def __create_default_config(self):
|
||||
@@ -557,6 +816,9 @@ class Reticulum:
|
||||
if path == "interface_stats":
|
||||
rpc_connection.send(self.get_interface_stats())
|
||||
|
||||
if path == "path_table":
|
||||
rpc_connection.send(self.get_path_table())
|
||||
|
||||
if path == "next_hop_if_name":
|
||||
rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"]))
|
||||
|
||||
@@ -569,7 +831,14 @@ class Reticulum:
|
||||
if path == "packet_snr":
|
||||
rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
|
||||
|
||||
if "drop" in call:
|
||||
path = call["drop"]
|
||||
|
||||
if path == "path":
|
||||
rpc_connection.send(self.drop_path(call["destination_hash"]))
|
||||
|
||||
rpc_connection.close()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error ocurred while handling RPC call from local client: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
@@ -580,7 +849,7 @@ class Reticulum:
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
else:
|
||||
stats = []
|
||||
interfaces = []
|
||||
for interface in RNS.Transport.interfaces:
|
||||
ifstats = {}
|
||||
|
||||
@@ -589,14 +858,86 @@ class Reticulum:
|
||||
else:
|
||||
ifstats["clients"] = None
|
||||
|
||||
if hasattr(interface, "b32"):
|
||||
if interface.b32 != None:
|
||||
ifstats["i2p_b32"] = interface.b32+".b32.i2p"
|
||||
else:
|
||||
ifstats["i2p_b32"] = None
|
||||
|
||||
if hasattr(interface, "bitrate"):
|
||||
if interface.bitrate != None:
|
||||
ifstats["bitrate"] = interface.bitrate
|
||||
else:
|
||||
ifstats["bitrate"] = None
|
||||
|
||||
if hasattr(interface, "peers"):
|
||||
if interface.peers != None:
|
||||
ifstats["peers"] = len(interface.peers)
|
||||
else:
|
||||
ifstats["peers"] = None
|
||||
|
||||
if hasattr(interface, "ifac_signature"):
|
||||
ifstats["ifac_signature"] = interface.ifac_signature
|
||||
ifstats["ifac_size"] = interface.ifac_size
|
||||
ifstats["ifac_netname"] = interface.ifac_netname
|
||||
else:
|
||||
ifstats["ifac_signature"] = None
|
||||
ifstats["ifac_size"] = None
|
||||
ifstats["ifac_netname"] = None
|
||||
|
||||
if hasattr(interface, "announce_queue"):
|
||||
if interface.announce_queue != None:
|
||||
ifstats["announce_queue"] = len(interface.announce_queue)
|
||||
else:
|
||||
ifstats["announce_queue"] = None
|
||||
|
||||
ifstats["name"] = str(interface)
|
||||
ifstats["rxb"] = interface.rxb
|
||||
ifstats["txb"] = interface.txb
|
||||
ifstats["status"] = interface.online
|
||||
stats.append(ifstats)
|
||||
ifstats["mode"] = interface.mode
|
||||
|
||||
interfaces.append(ifstats)
|
||||
|
||||
stats = {}
|
||||
stats["interfaces"] = interfaces
|
||||
if Reticulum.transport_enabled():
|
||||
stats["transport_id"] = RNS.Transport.identity.hash
|
||||
|
||||
return stats
|
||||
|
||||
def get_path_table(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
rpc_connection.send({"get": "path_table"})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else:
|
||||
path_table = []
|
||||
for dst_hash in RNS.Transport.destination_table:
|
||||
entry = {
|
||||
"hash": dst_hash,
|
||||
"timestamp": RNS.Transport.destination_table[dst_hash][0],
|
||||
"via": RNS.Transport.destination_table[dst_hash][1],
|
||||
"hops": RNS.Transport.destination_table[dst_hash][2],
|
||||
"expires": RNS.Transport.destination_table[dst_hash][3],
|
||||
"interface": str(RNS.Transport.destination_table[dst_hash][5]),
|
||||
}
|
||||
path_table.append(entry)
|
||||
|
||||
return path_table
|
||||
|
||||
def drop_path(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
rpc_connection.send({"drop": "path", "destination_hash": destination})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else:
|
||||
return RNS.Transport.expire_path(destination)
|
||||
|
||||
def get_next_hop_if_name(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
@@ -674,12 +1015,18 @@ __default_rns_config__ = '''# This is the default Reticulum config file.
|
||||
# You should probably edit it to include any additional,
|
||||
# interfaces and settings you might need.
|
||||
|
||||
# Only the most basic options are included in this default
|
||||
# configuration. To see a more verbose, and much longer,
|
||||
# configuration example, you can run the command:
|
||||
# rnsd --exampleconfig
|
||||
|
||||
|
||||
[reticulum]
|
||||
|
||||
# If you enable Transport, your system will route traffic
|
||||
# for other peers, pass announces and serve path requests.
|
||||
# This should be done for systems that are suited to act
|
||||
# as transport nodes, ie. if they are stationary and
|
||||
# This should only be done for systems that are suited to
|
||||
# act as transport nodes, ie. if they are stationary and
|
||||
# always-on. This directive is optional and can be removed
|
||||
# for brevity.
|
||||
|
||||
@@ -707,6 +1054,7 @@ share_instance = Yes
|
||||
shared_instance_port = 37428
|
||||
instance_control_port = 37429
|
||||
|
||||
|
||||
# You can configure Reticulum to panic and forcibly close
|
||||
# if an unrecoverable interface error occurs, such as the
|
||||
# hardware device for an interface disappearing. This is
|
||||
@@ -749,265 +1097,5 @@ loglevel = 4
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
|
||||
# The following example enables communication with other
|
||||
# local Reticulum peers using UDP broadcasts.
|
||||
|
||||
[[UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = False
|
||||
outgoing = True
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
forward_ip = 255.255.255.255
|
||||
forward_port = 4242
|
||||
|
||||
# The above configuration will allow communication
|
||||
# within the local broadcast domains of all local
|
||||
# IP interfaces.
|
||||
|
||||
# Instead of specifying listen_ip, listen_port,
|
||||
# forward_ip and forward_port, you can also bind
|
||||
# to a specific network device like below.
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
# Assuming the eth0 device has the address
|
||||
# 10.55.0.72/24, the above configuration would
|
||||
# be equivalent to the following manual setup.
|
||||
# Note that we are both listening and forwarding to
|
||||
# the broadcast address of the network segments.
|
||||
|
||||
# listen_ip = 10.55.0.255
|
||||
# listen_port = 4242
|
||||
# forward_ip = 10.55.0.255
|
||||
# forward_port = 4242
|
||||
|
||||
# You can of course also communicate only with
|
||||
# a single IP address
|
||||
|
||||
# listen_ip = 10.55.0.15
|
||||
# listen_port = 4242
|
||||
# forward_ip = 10.55.0.16
|
||||
# forward_port = 4242
|
||||
|
||||
|
||||
# This example demonstrates a TCP server interface.
|
||||
# It will listen for incoming connections on the
|
||||
# specified IP address and port number.
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = False
|
||||
outgoing = True
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
|
||||
# Alternatively you can bind to a specific IP
|
||||
|
||||
# listen_ip = 10.0.0.88
|
||||
# listen_port = 4242
|
||||
|
||||
# Or a specific network device
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
|
||||
# To connect to a TCP server interface, you would
|
||||
# naturally use the TCP client interface. Here's
|
||||
# an example. The target_host can either be an IP
|
||||
# address or a hostname
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = False
|
||||
outgoing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
|
||||
# Here's an example of how to add a LoRa interface
|
||||
# using the RNode LoRa transceiver.
|
||||
|
||||
[[RNode LoRa Interface]]
|
||||
type = RNodeInterface
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
|
||||
# Allow transmit on interface. Setting
|
||||
# this to false will create a listen-
|
||||
# only interface.
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
|
||||
# Set frequency to 867.2 MHz
|
||||
frequency = 867200000
|
||||
|
||||
# Set LoRa bandwidth to 125 KHz
|
||||
bandwidth = 125000
|
||||
|
||||
# Set TX power to 7 dBm (5 mW)
|
||||
txpower = 7
|
||||
|
||||
# Select spreading factor 8. Valid
|
||||
# range is 7 through 12, with 7
|
||||
# being the fastest and 12 having
|
||||
# the longest range.
|
||||
spreadingfactor = 8
|
||||
|
||||
# Select coding rate 5. Valid range
|
||||
# is 5 throough 8, with 5 being the
|
||||
# fastest, and 8 the longest range.
|
||||
codingrate = 5
|
||||
|
||||
# You can configure the RNode to send
|
||||
# out identification on the channel with
|
||||
# a set interval by configuring the
|
||||
# following two parameters. The trans-
|
||||
# ceiver will only ID if the set
|
||||
# interval has elapsed since it's last
|
||||
# actual transmission. The interval is
|
||||
# configured in seconds.
|
||||
# This option is commented out and not
|
||||
# used by default.
|
||||
# id_callsign = MYCALL-0
|
||||
# id_interval = 600
|
||||
|
||||
# For certain homebrew RNode interfaces
|
||||
# with low amounts of RAM, using packet
|
||||
# flow control can be useful. By default
|
||||
# it is disabled.
|
||||
flow_control = False
|
||||
|
||||
|
||||
# An example KISS modem interface. Useful for running
|
||||
# Reticulum over packet radio hardware.
|
||||
|
||||
[[Packet Radio KISS Interface]]
|
||||
type = KISSInterface
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
|
||||
# Allow transmit on interface.
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB1
|
||||
|
||||
# Set the serial baud-rate and other
|
||||
# configuration parameters.
|
||||
speed = 115200
|
||||
databits = 8
|
||||
parity = none
|
||||
stopbits = 1
|
||||
|
||||
# Set the modem preamble. A 150ms
|
||||
# preamble should be a reasonable
|
||||
# default, but may need to be
|
||||
# increased for radios with slow-
|
||||
# opening squelch and long TX/RX
|
||||
# turnaround
|
||||
preamble = 150
|
||||
|
||||
# Set the modem TX tail. In most
|
||||
# cases this should be kept as low
|
||||
# as possible to not waste airtime.
|
||||
txtail = 10
|
||||
|
||||
# Configure CDMA parameters. These
|
||||
# settings are reasonable defaults.
|
||||
persistence = 200
|
||||
slottime = 20
|
||||
|
||||
# You can configure the interface to send
|
||||
# out identification on the channel with
|
||||
# a set interval by configuring the
|
||||
# following two parameters. The KISS
|
||||
# interface will only ID if the set
|
||||
# interval has elapsed since it's last
|
||||
# actual transmission. The interval is
|
||||
# configured in seconds.
|
||||
# This option is commented out and not
|
||||
# used by default.
|
||||
# id_callsign = MYCALL-0
|
||||
# id_interval = 600
|
||||
|
||||
# Whether to use KISS flow-control.
|
||||
# This is useful for modems that have
|
||||
# a small internal packet buffer, but
|
||||
# support packet flow control instead.
|
||||
flow_control = false
|
||||
|
||||
|
||||
# If you're using Reticulum on amateur radio spectrum,
|
||||
# you might want to use the AX.25 KISS interface. This
|
||||
# way, Reticulum will automatically encapsulate it's
|
||||
# traffic in AX.25 and also identify your stations
|
||||
# transmissions with your callsign and SSID.
|
||||
#
|
||||
# Only do this if you really need to! Reticulum doesn't
|
||||
# need the AX.25 layer for anything, and it incurs extra
|
||||
# overhead on every packet to encapsulate in AX.25.
|
||||
#
|
||||
# A more efficient way is to use the plain KISS interface
|
||||
# with the beaconing functionality described above.
|
||||
|
||||
[[Packet Radio AX.25 KISS Interface]]
|
||||
type = AX25KISSInterface
|
||||
|
||||
# Set the station callsign and SSID
|
||||
callsign = NO1CLL
|
||||
ssid = 0
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
|
||||
# Allow transmit on interface.
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB2
|
||||
|
||||
# Set the serial baud-rate and other
|
||||
# configuration parameters.
|
||||
speed = 115200
|
||||
databits = 8
|
||||
parity = none
|
||||
stopbits = 1
|
||||
|
||||
# Whether to use KISS flow-control.
|
||||
# This is useful for modems with a
|
||||
# small internal packet buffer.
|
||||
flow_control = false
|
||||
|
||||
# Set the modem preamble. A 150ms
|
||||
# preamble should be a reasonable
|
||||
# default, but may need to be
|
||||
# increased for radios with slow-
|
||||
# opening squelch and long TX/RX
|
||||
# turnaround
|
||||
preamble = 150
|
||||
|
||||
# Set the modem TX tail. In most
|
||||
# cases this should be kept as low
|
||||
# as possible to not waste airtime.
|
||||
txtail = 10
|
||||
|
||||
# Configure CDMA parameters. These
|
||||
# settings are reasonable defaults.
|
||||
persistence = 200
|
||||
slottime = 20
|
||||
|
||||
'''.splitlines()
|
||||
|
||||
+309
-87
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import os
|
||||
import RNS
|
||||
import time
|
||||
@@ -30,11 +52,12 @@ class Transport:
|
||||
"""
|
||||
Maximum amount of hops that Reticulum will transport a packet.
|
||||
"""
|
||||
PATHFINDER_C = 2.0 # Decay constant
|
||||
|
||||
PATHFINDER_R = 1 # Retransmit retries
|
||||
PATHFINDER_T = 10 # Retry grace period
|
||||
PATHFINDER_RW = 10 # Random window for announce rebroadcast
|
||||
PATHFINDER_E = 60*60*24*7 # Path expiration in seconds
|
||||
PATHFINDER_G = 5 # Retry grace period
|
||||
PATHFINDER_RW = 0.5 # Random window for announce rebroadcast
|
||||
PATHFINDER_E = 60*60*24*7 # Path expiration of one week
|
||||
AP_PATH_TIME = 60*60*24 # Path expiration of one day for Access Point paths
|
||||
|
||||
# TODO: Calculate an optimal number for this in
|
||||
# various situations
|
||||
@@ -58,6 +81,7 @@ class Transport:
|
||||
# TODO: "destination_table" should really be renamed to "path_table"
|
||||
# Notes on memory usage: 1 megabyte of memory can store approximately
|
||||
# 55.100 path table entries or approximately 22.300 link table entries.
|
||||
|
||||
announce_table = {} # A table for storing announces currently waiting to be retransmitted
|
||||
destination_table = {} # A lookup table containing the next hop to a given destination
|
||||
reverse_table = {} # A lookup table for storing packet hashes used to return proofs and replies
|
||||
@@ -97,6 +121,7 @@ class Transport:
|
||||
|
||||
@staticmethod
|
||||
def start(reticulum_instance):
|
||||
Transport.jobs_running = True
|
||||
Transport.owner = reticulum_instance
|
||||
|
||||
if Transport.identity == None:
|
||||
@@ -131,6 +156,7 @@ class Transport:
|
||||
Transport.control_destinations.append(Transport.tunnel_synthesize_handler)
|
||||
Transport.control_hashes.append(Transport.tunnel_synthesize_destination.hash)
|
||||
|
||||
Transport.jobs_running = False
|
||||
thread = threading.Thread(target=Transport.jobloop)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
@@ -271,12 +297,12 @@ class Transport:
|
||||
for destination_hash in Transport.announce_table:
|
||||
announce_entry = Transport.announce_table[destination_hash]
|
||||
if announce_entry[2] > Transport.PATHFINDER_R:
|
||||
RNS.log("Dropping announce for "+RNS.prettyhexrep(destination_hash)+", retries exceeded", RNS.LOG_DEBUG)
|
||||
RNS.log("Completed announce processing for "+RNS.prettyhexrep(destination_hash)+", retry limit reached", RNS.LOG_EXTREME)
|
||||
Transport.announce_table.pop(destination_hash)
|
||||
break
|
||||
else:
|
||||
if time.time() > announce_entry[1]:
|
||||
announce_entry[1] = time.time() + math.pow(Transport.PATHFINDER_C, announce_entry[4]) + Transport.PATHFINDER_T + Transport.PATHFINDER_RW
|
||||
announce_entry[1] = time.time() + Transport.PATHFINDER_G + Transport.PATHFINDER_RW
|
||||
announce_entry[2] += 1
|
||||
packet = announce_entry[5]
|
||||
block_rebroadcasts = announce_entry[7]
|
||||
@@ -306,6 +332,7 @@ class Transport:
|
||||
RNS.log("Rebroadcasting announce as path response for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("Rebroadcasting announce for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG)
|
||||
|
||||
outgoing.append(new_packet)
|
||||
|
||||
# This handles an edge case where a peer sends a past
|
||||
@@ -363,7 +390,7 @@ class Transport:
|
||||
expires = tunnel_entry[3]
|
||||
if time.time() > expires:
|
||||
stale_tunnels.append(tunnel_id)
|
||||
RNS.log("Tunnel "+RNS.prettyhexrep(tunnel_id)+" timed out and was removed", RNS.LOG_DEBUG)
|
||||
RNS.log("Tunnel "+RNS.prettyhexrep(tunnel_id)+" timed out and was removed", RNS.LOG_EXTREME)
|
||||
else:
|
||||
stale_tunnel_paths = []
|
||||
tunnel_paths = tunnel_entry[2]
|
||||
@@ -372,7 +399,7 @@ class Transport:
|
||||
|
||||
if time.time() > tunnel_path_entry[0] + Transport.DESTINATION_TIMEOUT:
|
||||
stale_tunnel_paths.append(tunnel_path)
|
||||
RNS.log("Tunnel path to "+RNS.prettyhexrep(tunnel_path)+" timed out and was removed", RNS.LOG_DEBUG)
|
||||
RNS.log("Tunnel path to "+RNS.prettyhexrep(tunnel_path)+" timed out and was removed", RNS.LOG_EXTREME)
|
||||
|
||||
for tunnel_path in stale_tunnel_paths:
|
||||
tunnel_paths.pop(tunnel_path)
|
||||
@@ -381,9 +408,9 @@ class Transport:
|
||||
|
||||
if ti > 0:
|
||||
if ti == 1:
|
||||
RNS.log("Removed "+str(ti)+" tunnel path", RNS.LOG_DEBUG)
|
||||
RNS.log("Removed "+str(ti)+" tunnel path", RNS.LOG_EXTREME)
|
||||
else:
|
||||
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_DEBUG)
|
||||
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME)
|
||||
|
||||
|
||||
|
||||
@@ -394,9 +421,9 @@ class Transport:
|
||||
|
||||
if i > 0:
|
||||
if i == 1:
|
||||
RNS.log("Dropped "+str(i)+" reverse table entry", RNS.LOG_DEBUG)
|
||||
RNS.log("Released "+str(i)+" reverse table entry", RNS.LOG_EXTREME)
|
||||
else:
|
||||
RNS.log("Dropped "+str(i)+" reverse table entries", RNS.LOG_DEBUG)
|
||||
RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME)
|
||||
|
||||
|
||||
|
||||
@@ -407,9 +434,9 @@ class Transport:
|
||||
|
||||
if i > 0:
|
||||
if i == 1:
|
||||
RNS.log("Dropped "+str(i)+" link", RNS.LOG_DEBUG)
|
||||
RNS.log("Released "+str(i)+" link", RNS.LOG_EXTREME)
|
||||
else:
|
||||
RNS.log("Dropped "+str(i)+" links", RNS.LOG_DEBUG)
|
||||
RNS.log("Released "+str(i)+" links", RNS.LOG_EXTREME)
|
||||
|
||||
i = 0
|
||||
for destination_hash in stale_paths:
|
||||
@@ -418,9 +445,9 @@ class Transport:
|
||||
|
||||
if i > 0:
|
||||
if i == 1:
|
||||
RNS.log("Removed "+str(i)+" path", RNS.LOG_DEBUG)
|
||||
RNS.log("Removed "+str(i)+" path", RNS.LOG_EXTREME)
|
||||
else:
|
||||
RNS.log("Removed "+str(i)+" paths", RNS.LOG_DEBUG)
|
||||
RNS.log("Removed "+str(i)+" paths", RNS.LOG_EXTREME)
|
||||
|
||||
i = 0
|
||||
for tunnel_id in stale_tunnels:
|
||||
@@ -429,9 +456,9 @@ class Transport:
|
||||
|
||||
if i > 0:
|
||||
if i == 1:
|
||||
RNS.log("Removed "+str(i)+" tunnel", RNS.LOG_DEBUG)
|
||||
RNS.log("Removed "+str(i)+" tunnel", RNS.LOG_EXTREME)
|
||||
else:
|
||||
RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_DEBUG)
|
||||
RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_EXTREME)
|
||||
|
||||
Transport.tables_last_culled = time.time()
|
||||
|
||||
@@ -445,18 +472,40 @@ class Transport:
|
||||
for packet in outgoing:
|
||||
packet.send()
|
||||
|
||||
@staticmethod
|
||||
def transmit(interface, raw):
|
||||
try:
|
||||
if hasattr(interface, "ifac_identity") and interface.ifac_identity != None:
|
||||
# Calculate packet access code
|
||||
ifac = interface.ifac_identity.sign(raw)[-interface.ifac_size:]
|
||||
|
||||
# Set IFAC flag
|
||||
new_header = bytes([raw[0] | 0x80, raw[1]])
|
||||
|
||||
# Assemble new payload with IFAC and send it
|
||||
new_raw = new_header+ifac+raw[2:]
|
||||
interface.processOutgoing(new_raw)
|
||||
|
||||
else:
|
||||
interface.processOutgoing(raw)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while transmitting on "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
@staticmethod
|
||||
def outbound(packet):
|
||||
while (Transport.jobs_running):
|
||||
# TODO: Profile actual impact here on faster links
|
||||
sleep(0.01)
|
||||
|
||||
Transport.jobs_locked = True
|
||||
# TODO: This updateHash call might be redundant
|
||||
packet.update_hash()
|
||||
sent = False
|
||||
outbound_time = time.time()
|
||||
|
||||
# Check if we have a known path for the destination in the path table
|
||||
if packet.packet_type != RNS.Packet.ANNOUNCE and packet.destination_hash in Transport.destination_table:
|
||||
if packet.packet_type != RNS.Packet.ANNOUNCE and packet.destination.type != RNS.Destination.PLAIN and packet.destination.type != RNS.Destination.GROUP and packet.destination_hash in Transport.destination_table:
|
||||
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
||||
|
||||
# If there's more than one hop to the destination, and we know
|
||||
@@ -472,7 +521,7 @@ class Transport:
|
||||
new_raw += packet.raw[1:2]
|
||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||
new_raw += packet.raw[2:]
|
||||
outbound_interface.processOutgoing(new_raw)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
sent = True
|
||||
|
||||
@@ -491,7 +540,7 @@ class Transport:
|
||||
new_raw += packet.raw[1:2]
|
||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||
new_raw += packet.raw[2:]
|
||||
outbound_interface.processOutgoing(new_raw)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
sent = True
|
||||
|
||||
@@ -499,7 +548,7 @@ class Transport:
|
||||
# directly reachable, and also on which interface, so we
|
||||
# simply transmit the packet directly on that one.
|
||||
else:
|
||||
outbound_interface.processOutgoing(packet.raw)
|
||||
Transport.transmit(outbound_interface, packet.raw)
|
||||
sent = True
|
||||
|
||||
# If we don't have a known path for the destination, we'll
|
||||
@@ -511,20 +560,88 @@ class Transport:
|
||||
for interface in Transport.interfaces:
|
||||
if interface.OUT:
|
||||
should_transmit = True
|
||||
|
||||
if packet.destination.type == RNS.Destination.LINK:
|
||||
if packet.destination.status == RNS.Link.CLOSED:
|
||||
should_transmit = False
|
||||
if interface != packet.destination.attached_interface:
|
||||
should_transmit = False
|
||||
|
||||
if packet.attached_interface != None and interface != packet.attached_interface:
|
||||
should_transmit = False
|
||||
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
if packet.attached_interface == None:
|
||||
if interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to AP mode", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
|
||||
else:
|
||||
# Currently, annouces originating locally are always
|
||||
# allowed, and do not conform to bandwidth caps.
|
||||
# TODO: Rethink whether this is actually optimal.
|
||||
if packet.hops > 0:
|
||||
|
||||
if not hasattr(interface, "announce_cap"):
|
||||
interface.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
|
||||
|
||||
if not hasattr(interface, "announce_allowed_at"):
|
||||
interface.announce_allowed_at = 0
|
||||
|
||||
if not hasattr(interface, "announce_queue"):
|
||||
interface.announce_queue = []
|
||||
|
||||
queued_announces = True if len(interface.announce_queue) > 0 else False
|
||||
|
||||
if not queued_announces and outbound_time > interface.announce_allowed_at:
|
||||
tx_time = (len(packet.raw)*8) / interface.bitrate
|
||||
wait_time = (tx_time / interface.announce_cap)
|
||||
interface.announce_allowed_at = outbound_time + wait_time
|
||||
|
||||
# TODO: Clean
|
||||
# wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
# RNS.log("Next announce on "+str(interface)+" allowed in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
|
||||
else:
|
||||
should_transmit = False
|
||||
if not len(interface.announce_queue) >= RNS.Reticulum.MAX_QUEUED_ANNOUNCES:
|
||||
entry = {"time": outbound_time, "hops": packet.hops, "raw": packet.raw}
|
||||
queued_announces = True if len(interface.announce_queue) > 0 else False
|
||||
interface.announce_queue.append(entry)
|
||||
|
||||
if not queued_announces:
|
||||
wait_time = max(interface.announce_allowed_at - time.time(), 0)
|
||||
timer = threading.Timer(wait_time, interface.process_announce_queue)
|
||||
timer.start()
|
||||
|
||||
wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
ql_str = str(len(interface.announce_queue))
|
||||
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
|
||||
else:
|
||||
wait_time = max(interface.announce_allowed_at - time.time(), 0)
|
||||
wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
ql_str = str(len(interface.announce_queue))
|
||||
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
if should_transmit:
|
||||
if not stored_hash:
|
||||
Transport.packet_hashlist.append(packet.packet_hash)
|
||||
stored_hash = True
|
||||
|
||||
interface.processOutgoing(packet.raw)
|
||||
def send_packet():
|
||||
Transport.transmit(interface, packet.raw)
|
||||
|
||||
thread = threading.Thread(target=send_packet)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
sent = True
|
||||
|
||||
if sent:
|
||||
@@ -565,27 +682,95 @@ class Transport:
|
||||
return True
|
||||
if packet.context == RNS.Packet.CACHE_REQUEST:
|
||||
return True
|
||||
|
||||
if packet.destination_type == RNS.Destination.PLAIN:
|
||||
return True
|
||||
if packet.packet_type != RNS.Packet.ANNOUNCE:
|
||||
if packet.hops > 1:
|
||||
RNS.log("Dropped PLAIN packet "+RNS.prettyhexrep(packet.hash)+" with "+str(packet.hops)+" hops", RNS.LOG_DEBUG)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
RNS.log("Dropped invalid PLAIN announce packet", RNS.LOG_DEBUG)
|
||||
return False
|
||||
|
||||
if packet.destination_type == RNS.Destination.GROUP:
|
||||
if packet.packet_type != RNS.Packet.ANNOUNCE:
|
||||
if packet.hops > 1:
|
||||
RNS.log("Dropped GROUP packet "+RNS.prettyhexrep(packet.hash)+" with "+str(packet.hops)+" hops", RNS.LOG_DEBUG)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
RNS.log("Dropped invalid GROUP announce packet", RNS.LOG_DEBUG)
|
||||
return False
|
||||
|
||||
if not packet.packet_hash in Transport.packet_hashlist:
|
||||
return True
|
||||
else:
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
return True
|
||||
if packet.destination_type == RNS.Destination.SINGLE:
|
||||
return True
|
||||
else:
|
||||
RNS.log("Dropped invalid announce packet", RNS.LOG_DEBUG)
|
||||
return False
|
||||
|
||||
RNS.log("Filtered packet with hash "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_DEBUG)
|
||||
RNS.log("Filtered packet with hash "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def inbound(raw, interface=None):
|
||||
# If interface access codes are enabled,
|
||||
# we must authenticate each packet.
|
||||
if interface != None and hasattr(interface, "ifac_identity") and interface.ifac_identity != None:
|
||||
# Check that IFAC flag is set
|
||||
if raw[0] & 0x80 == 0x80:
|
||||
if len(raw) > 2+interface.ifac_size:
|
||||
# Extract IFAC
|
||||
ifac = raw[2:2+interface.ifac_size]
|
||||
|
||||
# Unset IFAC flag
|
||||
new_header = bytes([raw[0] & 0x7f, raw[1]])
|
||||
|
||||
# Re-assemble packet
|
||||
new_raw = new_header+raw[2+interface.ifac_size:]
|
||||
|
||||
# Calculate expected IFAC
|
||||
expected_ifac = interface.ifac_identity.sign(new_raw)[-interface.ifac_size:]
|
||||
|
||||
# Check it
|
||||
if ifac == expected_ifac:
|
||||
raw = new_raw
|
||||
else:
|
||||
return
|
||||
|
||||
else:
|
||||
return
|
||||
|
||||
else:
|
||||
# If the IFAC flag is not set, but should be,
|
||||
# drop the packet.
|
||||
return
|
||||
|
||||
else:
|
||||
# If the interface does not have IFAC enabled,
|
||||
# check the received packet IFAC flag.
|
||||
if raw[0] & 0x80 == 0x80:
|
||||
# If the flag is set, drop the packet
|
||||
return
|
||||
|
||||
while (Transport.jobs_running):
|
||||
sleep(0.01)
|
||||
|
||||
if Transport.identity == None:
|
||||
return
|
||||
|
||||
Transport.jobs_locked = True
|
||||
|
||||
packet = RNS.Packet(None, raw)
|
||||
packet.unpack()
|
||||
if not packet.unpack():
|
||||
return
|
||||
|
||||
packet.receiving_interface = interface
|
||||
packet.hops += 1
|
||||
|
||||
@@ -637,12 +822,12 @@ class Transport:
|
||||
if from_local_client:
|
||||
for interface in Transport.interfaces:
|
||||
if interface != packet.receiving_interface:
|
||||
interface.processOutgoing(packet.raw)
|
||||
Transport.transmit(interface, packet.raw)
|
||||
# If the packet was not from a local client, send
|
||||
# it directly to all local clients
|
||||
else:
|
||||
for interface in Transport.local_client_interfaces:
|
||||
interface.processOutgoing(packet.raw)
|
||||
Transport.transmit(interface, packet.raw)
|
||||
|
||||
|
||||
# General transport handling. Takes care of directing
|
||||
@@ -695,7 +880,7 @@ class Transport:
|
||||
new_raw += packet.raw[2:]
|
||||
|
||||
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
||||
outbound_interface.processOutgoing(new_raw)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
|
||||
if packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||
@@ -723,7 +908,7 @@ class Transport:
|
||||
# TODO: There should probably be some kind of REJECT
|
||||
# mechanism here, to signal to the source that their
|
||||
# expected path failed.
|
||||
RNS.log("Got packet in transport, but no known path to final destination "+RNS.prettyhexrep(packet.destination_hash)+". Dropping packet.", RNS.LOG_DEBUG)
|
||||
RNS.log("Got packet in transport, but no known path to final destination "+RNS.prettyhexrep(packet.destination_hash)+". Dropping packet.", RNS.LOG_EXTREME)
|
||||
|
||||
# Link transport handling. Directs packets according
|
||||
# to entries in the link tables
|
||||
@@ -756,7 +941,7 @@ class Transport:
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
outbound_interface.processOutgoing(new_raw)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.link_table[packet.destination_hash][0] = time.time()
|
||||
else:
|
||||
pass
|
||||
@@ -787,7 +972,7 @@ class Transport:
|
||||
if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0:
|
||||
now = time.time()
|
||||
if now < announce_entry[1]:
|
||||
RNS.log("Rebroadcasted announce for "+RNS.prettyhexrep(packet.destination_hash)+" has been passed on to next node, no further tries needed", RNS.LOG_DEBUG)
|
||||
RNS.log("Rebroadcasted announce for "+RNS.prettyhexrep(packet.destination_hash)+" has been passed on to another node, no further tries needed", RNS.LOG_DEBUG)
|
||||
Transport.announce_table.pop(packet.destination_hash)
|
||||
|
||||
else:
|
||||
@@ -834,7 +1019,7 @@ class Transport:
|
||||
break
|
||||
|
||||
if (now >= path_expires):
|
||||
# We also check that the announce hash is
|
||||
# We also check that the announce is
|
||||
# different from ones we've already heard,
|
||||
# to avoid loops in the network
|
||||
if not random_blob in random_blobs:
|
||||
@@ -860,12 +1045,17 @@ class Transport:
|
||||
if should_add:
|
||||
now = time.time()
|
||||
retries = 0
|
||||
expires = now + Transport.PATHFINDER_E
|
||||
announce_hops = packet.hops
|
||||
local_rebroadcasts = 0
|
||||
block_rebroadcasts = False
|
||||
attached_interface = None
|
||||
retransmit_timeout = now + math.pow(Transport.PATHFINDER_C, packet.hops) + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||
|
||||
retransmit_timeout = now + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||
|
||||
if packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
expires = now + Transport.AP_PATH_TIME
|
||||
else:
|
||||
expires = now + Transport.PATHFINDER_E
|
||||
|
||||
random_blobs.append(random_blob)
|
||||
|
||||
@@ -959,7 +1149,7 @@ class Transport:
|
||||
|
||||
destination_table_entry = [now, received_from, announce_hops, expires, random_blobs, packet.receiving_interface, packet]
|
||||
Transport.destination_table[packet.destination_hash] = destination_table_entry
|
||||
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_VERBOSE)
|
||||
RNS.log("Destination "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
|
||||
|
||||
# If the receiving interface is a tunnel, we add the
|
||||
# announce to the tunnels table
|
||||
@@ -969,7 +1159,7 @@ class Transport:
|
||||
paths[packet.destination_hash] = destination_table_entry
|
||||
expires = time.time() + Transport.DESTINATION_TIMEOUT
|
||||
tunnel_entry[3] = expires
|
||||
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" associated with tunnel "+RNS.prettyhexrep(packet.receiving_interface.tunnel_id), RNS.LOG_VERBOSE)
|
||||
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" associated with tunnel "+RNS.prettyhexrep(packet.receiving_interface.tunnel_id), RNS.LOG_DEBUG)
|
||||
|
||||
# Call externally registered callbacks from apps
|
||||
# wanting to know when an announce arrives
|
||||
@@ -1044,7 +1234,7 @@ class Transport:
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
Transport.link_table[packet.destination_hash][7] = True
|
||||
link_entry[4].processOutgoing(new_raw)
|
||||
Transport.transmit(link_entry[4], new_raw)
|
||||
else:
|
||||
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||
else:
|
||||
@@ -1077,7 +1267,7 @@ class Transport:
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
reverse_entry[0].processOutgoing(new_raw)
|
||||
Transport.transmit(reverse_entry[0], new_raw)
|
||||
else:
|
||||
RNS.log("Proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||
|
||||
@@ -1198,10 +1388,6 @@ class Transport:
|
||||
RNS.log("Removing path to "+RNS.prettyhexrep(deprecated_path)+" from tunnel "+RNS.prettyhexrep(tunnel_id), RNS.LOG_DEBUG)
|
||||
paths.pop(deprecated_path)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def register_destination(destination):
|
||||
destination.MTU = RNS.Reticulum.MTU
|
||||
@@ -1396,41 +1582,67 @@ class Transport:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def request_path(destination_hash):
|
||||
def expire_path(destination_hash):
|
||||
if destination_hash in Transport.destination_table:
|
||||
Transport.destination_table[destination_hash][0] = 0
|
||||
Transport.tables_last_culled = 0
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def request_path(destination_hash, on_interface=None):
|
||||
"""
|
||||
Requests a path to the destination from the network. If
|
||||
another reachable peer on the network knows a path, it
|
||||
will announce it.
|
||||
|
||||
:param destination_hash: A destination hash as *bytes*.
|
||||
:param on_interface: If specified, the path request will only be sent on this interface. In normal use, Reticulum handles this automatically, and this parameter should not be used.
|
||||
"""
|
||||
path_request_data = destination_hash + RNS.Identity.get_random_hash()
|
||||
path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||
packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1)
|
||||
packet.send()
|
||||
if RNS.Reticulum.transport_enabled():
|
||||
path_request_data = destination_hash+Transport.identity.hash+RNS.Identity.get_random_hash()
|
||||
else:
|
||||
path_request_data = destination_hash+RNS.Identity.get_random_hash()
|
||||
|
||||
@staticmethod
|
||||
def request_path_on_interface(destination_hash, interface):
|
||||
path_request_data = destination_hash + RNS.Identity.get_random_hash()
|
||||
path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||
packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1, attached_interface = interface)
|
||||
packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1, attached_interface = on_interface)
|
||||
packet.send()
|
||||
|
||||
@staticmethod
|
||||
def path_request_handler(data, packet):
|
||||
try:
|
||||
# If there is at least bytes enough for a destination
|
||||
# hash in the packet, we assume those bytes are the
|
||||
# destination being requested.
|
||||
if len(data) >= RNS.Identity.TRUNCATED_HASHLENGTH//8:
|
||||
# If there is also enough bytes for a trasport
|
||||
# instance ID and at least one random byte, we
|
||||
# assume the next bytes to be the trasport ID
|
||||
# of the requesting transport instance.
|
||||
if len(data) > (RNS.Identity.TRUNCATED_HASHLENGTH//8)*2:
|
||||
requesting_transport_instance = data[RNS.Identity.TRUNCATED_HASHLENGTH//8:(RNS.Identity.TRUNCATED_HASHLENGTH//8)*2]
|
||||
else:
|
||||
requesting_transport_instance = None
|
||||
|
||||
Transport.path_request(
|
||||
data[:RNS.Identity.TRUNCATED_HASHLENGTH//8],
|
||||
Transport.from_local_client(packet),
|
||||
packet.receiving_interface
|
||||
packet.receiving_interface,
|
||||
requesting_transport_instance,
|
||||
)
|
||||
except Exception as e:
|
||||
RNS.log("Error while handling path request. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
@staticmethod
|
||||
def path_request(destination_hash, is_from_local_client, attached_interface):
|
||||
RNS.log("Path request for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
|
||||
def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None):
|
||||
if attached_interface != None:
|
||||
interface_str = " on "+str(attached_interface)
|
||||
else:
|
||||
interface_str = ""
|
||||
|
||||
# TODO: Clean
|
||||
# RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG)
|
||||
|
||||
destination_exists_on_local_client = False
|
||||
if len(Transport.local_client_interfaces) > 0:
|
||||
@@ -1443,54 +1655,67 @@ class Transport:
|
||||
|
||||
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
|
||||
if local_destination != None:
|
||||
RNS.log("Destination is local to this system, announcing", RNS.LOG_DEBUG)
|
||||
local_destination.announce(path_response=True)
|
||||
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):
|
||||
RNS.log("Path found, inserting announce for transmission", RNS.LOG_DEBUG)
|
||||
|
||||
packet = Transport.destination_table[destination_hash][6]
|
||||
next_hop = Transport.destination_table[destination_hash][1]
|
||||
received_from = Transport.destination_table[destination_hash][5]
|
||||
|
||||
now = time.time()
|
||||
retries = Transport.PATHFINDER_R
|
||||
local_rebroadcasts = 0
|
||||
block_rebroadcasts = True
|
||||
announce_hops = packet.hops
|
||||
|
||||
if is_from_local_client:
|
||||
retransmit_timeout = now
|
||||
|
||||
if requestor_transport_id != None and next_hop == requestor_transport_id:
|
||||
# TODO: Find a bandwidth efficient way to invalidate our
|
||||
# known path on this signal. The obvious way of signing
|
||||
# path requests with transport instance keys is quite
|
||||
# inefficient. There is probably a better way. Doing
|
||||
# path invalidation here would decrease the network
|
||||
# convergence time.
|
||||
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG)
|
||||
else:
|
||||
# TODO: Look at this timing
|
||||
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG)
|
||||
|
||||
# This handles an edge case where a peer sends a past
|
||||
# request for a destination just after an announce for
|
||||
# said destination has arrived, but before it has been
|
||||
# rebroadcast locally. In such a case the actual announce
|
||||
# is temporarily held, and then reinserted when the path
|
||||
# request has been served to the peer.
|
||||
if packet.destination_hash in Transport.announce_table:
|
||||
held_entry = Transport.announce_table[packet.destination_hash]
|
||||
Transport.held_announces[packet.destination_hash] = held_entry
|
||||
|
||||
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
|
||||
now = time.time()
|
||||
retries = Transport.PATHFINDER_R
|
||||
local_rebroadcasts = 0
|
||||
block_rebroadcasts = True
|
||||
announce_hops = packet.hops
|
||||
|
||||
if is_from_local_client:
|
||||
retransmit_timeout = now
|
||||
else:
|
||||
# TODO: Look at this timing
|
||||
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||
|
||||
# This handles an edge case where a peer sends a past
|
||||
# request for a destination just after an announce for
|
||||
# said destination has arrived, but before it has been
|
||||
# rebroadcast locally. In such a case the actual announce
|
||||
# is temporarily held, and then reinserted when the path
|
||||
# request has been served to the peer.
|
||||
if packet.destination_hash in Transport.announce_table:
|
||||
held_entry = Transport.announce_table[packet.destination_hash]
|
||||
Transport.held_announces[packet.destination_hash] = held_entry
|
||||
|
||||
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
|
||||
|
||||
elif is_from_local_client:
|
||||
# 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)
|
||||
for interface in Transport.interfaces:
|
||||
if not interface == attached_interface:
|
||||
Transport.request_path_on_interface(destination_hash, interface)
|
||||
Transport.request_path(destination_hash, interface)
|
||||
|
||||
elif not is_from_local_client and len(Transport.local_client_interfaces) > 0:
|
||||
# Forward the path request on all local
|
||||
# client interfaces
|
||||
RNS.log("Forwarding path request for "+RNS.prettyhexrep(destination_hash)+interface_str+" to local clients", RNS.LOG_DEBUG)
|
||||
for interface in Transport.local_client_interfaces:
|
||||
Transport.request_path_on_interface(destination_hash, interface)
|
||||
Transport.request_path(destination_hash, interface)
|
||||
|
||||
else:
|
||||
RNS.log("No known path to requested destination, ignoring request", RNS.LOG_DEBUG)
|
||||
RNS.log("Ignoring path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", no path known", RNS.LOG_DEBUG)
|
||||
|
||||
@staticmethod
|
||||
def from_local_client(packet):
|
||||
@@ -1524,7 +1749,6 @@ class Transport:
|
||||
for interface in Transport.local_client_interfaces:
|
||||
interface.detach()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def shared_connection_disappeared():
|
||||
for link in Transport.active_links:
|
||||
@@ -1541,7 +1765,6 @@ class Transport:
|
||||
Transport.announce_handlers = []
|
||||
Transport.tunnels = {}
|
||||
|
||||
|
||||
@staticmethod
|
||||
def shared_connection_reappeared():
|
||||
if Transport.owner.is_connected_to_shared_instance:
|
||||
@@ -1549,7 +1772,6 @@ class Transport:
|
||||
if registered_destination.type == RNS.Destination.SINGLE:
|
||||
registered_destination.announce(path_response=True)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def exit_handler():
|
||||
try:
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
|
||||
+124
-33
@@ -1,5 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import sys
|
||||
import time
|
||||
@@ -8,44 +30,81 @@ import argparse
|
||||
from RNS._version import __version__
|
||||
|
||||
|
||||
def program_setup(configdir, destination_hexhash, verbosity):
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(destination_hexhash) != dest_len:
|
||||
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
def program_setup(configdir, table, drop, destination_hexhash, verbosity, timeout):
|
||||
if table:
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
table = sorted(reticulum.get_path_table(), key=lambda e: (e["interface"], e["hops"]) )
|
||||
|
||||
for path in table:
|
||||
exp_str = RNS.timestamp_str(path["expires"])
|
||||
if path["hops"] == 1:
|
||||
m_str = " "
|
||||
else:
|
||||
m_str = "s"
|
||||
print(RNS.prettyhexrep(path["hash"])+" is "+str(path["hops"])+" hop"+m_str+" away via "+RNS.prettyhexrep(path["via"])+" on "+path["interface"]+" expires "+RNS.timestamp_str(path["expires"]))
|
||||
|
||||
elif drop:
|
||||
try:
|
||||
destination_hash = bytes.fromhex(destination_hexhash)
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(destination_hexhash) != dest_len:
|
||||
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(destination_hexhash)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit()
|
||||
print(str(e))
|
||||
exit()
|
||||
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
RNS.Transport.request_path(destination_hash)
|
||||
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||
sys.stdout.flush()
|
||||
if reticulum.drop_path(destination_hash):
|
||||
print("Dropped path to "+RNS.prettyhexrep(destination_hash))
|
||||
else:
|
||||
print("Unable to drop path to "+RNS.prettyhexrep(destination_hash)+". Does it exist?")
|
||||
|
||||
i = 0
|
||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||
while not RNS.Transport.has_path(destination_hash):
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
hops = RNS.Transport.hops_to(destination_hash)
|
||||
next_hop = RNS.prettyhexrep(reticulum.get_next_hop(destination_hash))
|
||||
next_hop_interface = reticulum.get_next_hop_if_name(destination_hash)
|
||||
|
||||
if hops != 1:
|
||||
ms = "s"
|
||||
else:
|
||||
ms = ""
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(destination_hexhash) != dest_len:
|
||||
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(destination_hexhash)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit()
|
||||
|
||||
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface)
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
RNS.Transport.request_path(destination_hash)
|
||||
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||
sys.stdout.flush()
|
||||
|
||||
i = 0
|
||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||
limit = time.time()+timeout
|
||||
while not RNS.Transport.has_path(destination_hash) and time.time()<limit:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
if RNS.Transport.has_path(destination_hash):
|
||||
hops = RNS.Transport.hops_to(destination_hash)
|
||||
next_hop = RNS.prettyhexrep(reticulum.get_next_hop(destination_hash))
|
||||
next_hop_interface = reticulum.get_next_hop_if_name(destination_hash)
|
||||
|
||||
if hops != 1:
|
||||
ms = "s"
|
||||
else:
|
||||
ms = ""
|
||||
|
||||
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface)
|
||||
else:
|
||||
print("\r \rPath not found")
|
||||
|
||||
|
||||
def main():
|
||||
@@ -65,6 +124,31 @@ def main():
|
||||
version="rnpath {version}".format(version=__version__)
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--table",
|
||||
action="store_true",
|
||||
help="show all known paths",
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--drop",
|
||||
action="store_true",
|
||||
help="remove the path to a destination",
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
action="store",
|
||||
metavar="seconds",
|
||||
type=float,
|
||||
help="timeout before giving up",
|
||||
default=15
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"destination",
|
||||
nargs="?",
|
||||
@@ -82,12 +166,19 @@ def main():
|
||||
else:
|
||||
configarg = None
|
||||
|
||||
if not args.destination:
|
||||
if not args.table and not args.destination:
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
else:
|
||||
program_setup(configdir = configarg, destination_hexhash = args.destination, verbosity = args.verbose)
|
||||
program_setup(
|
||||
configdir = configarg,
|
||||
table = args.table,
|
||||
drop = args.drop,
|
||||
destination_hexhash = args.destination,
|
||||
verbosity = args.verbose,
|
||||
timeout = args.w,
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("")
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import os
|
||||
import sys
|
||||
|
||||
+368
-1
@@ -1,5 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import argparse
|
||||
import time
|
||||
@@ -27,10 +49,15 @@ def main():
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||
parser.add_argument('-q', '--quiet', action='count', default=0)
|
||||
parser.add_argument('-s', '--service', action='store_true', default=False, help="rnsd is running as a service and should log to file")
|
||||
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
||||
parser.add_argument("--version", action="version", version="rnsd {version}".format(version=__version__))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.exampleconfig:
|
||||
print(__example_rns_config__)
|
||||
exit()
|
||||
|
||||
if args.config:
|
||||
configarg = args.config
|
||||
else:
|
||||
@@ -42,5 +69,345 @@ def main():
|
||||
print("")
|
||||
exit()
|
||||
|
||||
__example_rns_config__ = '''# This is an example Reticulum config file.
|
||||
# You should probably edit it to include any additional,
|
||||
# interfaces and settings you might need.
|
||||
|
||||
[reticulum]
|
||||
|
||||
# If you enable Transport, your system will route traffic
|
||||
# for other peers, pass announces and serve path requests.
|
||||
# This should be done for systems that are suited to act
|
||||
# as transport nodes, ie. if they are stationary and
|
||||
# always-on. This directive is optional and can be removed
|
||||
# for brevity.
|
||||
|
||||
enable_transport = False
|
||||
|
||||
|
||||
# By default, the first program to launch the Reticulum
|
||||
# Network Stack will create a shared instance, that other
|
||||
# programs can communicate with. Only the shared instance
|
||||
# opens all the configured interfaces directly, and other
|
||||
# local programs communicate with the shared instance over
|
||||
# a local socket. This is completely transparent to the
|
||||
# user, and should generally be turned on. This directive
|
||||
# is optional and can be removed for brevity.
|
||||
|
||||
share_instance = Yes
|
||||
|
||||
|
||||
# If you want to run multiple *different* shared instances
|
||||
# on the same system, you will need to specify different
|
||||
# shared instance ports for each. The defaults are given
|
||||
# below, and again, these options can be left out if you
|
||||
# don't need them.
|
||||
|
||||
shared_instance_port = 37428
|
||||
instance_control_port = 37429
|
||||
|
||||
# You can configure Reticulum to panic and forcibly close
|
||||
# if an unrecoverable interface error occurs, such as the
|
||||
# hardware device for an interface disappearing. This is
|
||||
# an optional directive, and can be left out for brevity.
|
||||
# This behaviour is disabled by default.
|
||||
|
||||
panic_on_interface_error = No
|
||||
|
||||
|
||||
[logging]
|
||||
# Valid log levels are 0 through 7:
|
||||
# 0: Log only critical information
|
||||
# 1: Log errors and lower log levels
|
||||
# 2: Log warnings and lower log levels
|
||||
# 3: Log notices and lower log levels
|
||||
# 4: Log info and lower (this is the default)
|
||||
# 5: Verbose logging
|
||||
# 6: Debug logging
|
||||
# 7: Extreme logging
|
||||
|
||||
loglevel = 4
|
||||
|
||||
|
||||
# The interfaces section defines the physical and virtual
|
||||
# interfaces Reticulum will use to communicate on. This
|
||||
# section will contain examples for a variety of interface
|
||||
# types. You can modify these or use them as a basis for
|
||||
# your own config, or simply remove the unused ones.
|
||||
|
||||
[interfaces]
|
||||
|
||||
# This interface enables communication with other
|
||||
# link-local Reticulum nodes over UDP. It does not
|
||||
# need any functional IP infrastructure like routers
|
||||
# or DHCP servers, but will require that at least link-
|
||||
# local IPv6 is enabled in your operating system, which
|
||||
# should be enabled by default in almost any OS. See
|
||||
# the Reticulum Manual for more configuration options.
|
||||
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
|
||||
|
||||
# The following example enables communication with other
|
||||
# local Reticulum peers using UDP broadcasts.
|
||||
|
||||
[[UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = False
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
forward_ip = 255.255.255.255
|
||||
forward_port = 4242
|
||||
|
||||
# The above configuration will allow communication
|
||||
# within the local broadcast domains of all local
|
||||
# IP interfaces.
|
||||
|
||||
# Instead of specifying listen_ip, listen_port,
|
||||
# forward_ip and forward_port, you can also bind
|
||||
# to a specific network device like below.
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
# Assuming the eth0 device has the address
|
||||
# 10.55.0.72/24, the above configuration would
|
||||
# be equivalent to the following manual setup.
|
||||
# Note that we are both listening and forwarding to
|
||||
# the broadcast address of the network segments.
|
||||
|
||||
# listen_ip = 10.55.0.255
|
||||
# listen_port = 4242
|
||||
# forward_ip = 10.55.0.255
|
||||
# forward_port = 4242
|
||||
|
||||
# You can of course also communicate only with
|
||||
# a single IP address
|
||||
|
||||
# listen_ip = 10.55.0.15
|
||||
# listen_port = 4242
|
||||
# forward_ip = 10.55.0.16
|
||||
# forward_port = 4242
|
||||
|
||||
|
||||
# This example demonstrates a TCP server interface.
|
||||
# It will listen for incoming connections on the
|
||||
# specified IP address and port number.
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = False
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
|
||||
# Alternatively you can bind to a specific IP
|
||||
|
||||
# listen_ip = 10.0.0.88
|
||||
# listen_port = 4242
|
||||
|
||||
# Or a specific network device
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
|
||||
# To connect to a TCP server interface, you would
|
||||
# naturally use the TCP client interface. Here's
|
||||
# an example. The target_host can either be an IP
|
||||
# address or a hostname
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = False
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
|
||||
# This example shows how to make your Reticulum
|
||||
# instance available over I2P, and connect to
|
||||
# another I2P peer. Please be aware that you
|
||||
# must have an I2P router running on your system
|
||||
# with the SAMv3 API enabled for this to work.
|
||||
|
||||
[[I2P]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
connectable = yes
|
||||
peers = 5urvjicpzi7q3ybztsef4i5ow2aq4soktfj7zedz53s47r54jnqq.b32.i2p
|
||||
|
||||
|
||||
# Here's an example of how to add a LoRa interface
|
||||
# using the RNode LoRa transceiver.
|
||||
|
||||
[[RNode LoRa Interface]]
|
||||
type = RNodeInterface
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
|
||||
# Set frequency to 867.2 MHz
|
||||
frequency = 867200000
|
||||
|
||||
# Set LoRa bandwidth to 125 KHz
|
||||
bandwidth = 125000
|
||||
|
||||
# Set TX power to 7 dBm (5 mW)
|
||||
txpower = 7
|
||||
|
||||
# Select spreading factor 8. Valid
|
||||
# range is 7 through 12, with 7
|
||||
# being the fastest and 12 having
|
||||
# the longest range.
|
||||
spreadingfactor = 8
|
||||
|
||||
# Select coding rate 5. Valid range
|
||||
# is 5 throough 8, with 5 being the
|
||||
# fastest, and 8 the longest range.
|
||||
codingrate = 5
|
||||
|
||||
# You can configure the RNode to send
|
||||
# out identification on the channel with
|
||||
# a set interval by configuring the
|
||||
# following two parameters. The trans-
|
||||
# ceiver will only ID if the set
|
||||
# interval has elapsed since it's last
|
||||
# actual transmission. The interval is
|
||||
# configured in seconds.
|
||||
# This option is commented out and not
|
||||
# used by default.
|
||||
# id_callsign = MYCALL-0
|
||||
# id_interval = 600
|
||||
|
||||
# For certain homebrew RNode interfaces
|
||||
# with low amounts of RAM, using packet
|
||||
# flow control can be useful. By default
|
||||
# it is disabled.
|
||||
flow_control = False
|
||||
|
||||
|
||||
# An example KISS modem interface. Useful for running
|
||||
# Reticulum over packet radio hardware.
|
||||
|
||||
[[Packet Radio KISS Interface]]
|
||||
type = KISSInterface
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB1
|
||||
|
||||
# Set the serial baud-rate and other
|
||||
# configuration parameters.
|
||||
speed = 115200
|
||||
databits = 8
|
||||
parity = none
|
||||
stopbits = 1
|
||||
|
||||
# Set the modem preamble. A 150ms
|
||||
# preamble should be a reasonable
|
||||
# default, but may need to be
|
||||
# increased for radios with slow-
|
||||
# opening squelch and long TX/RX
|
||||
# turnaround
|
||||
preamble = 150
|
||||
|
||||
# Set the modem TX tail. In most
|
||||
# cases this should be kept as low
|
||||
# as possible to not waste airtime.
|
||||
txtail = 10
|
||||
|
||||
# Configure CDMA parameters. These
|
||||
# settings are reasonable defaults.
|
||||
persistence = 200
|
||||
slottime = 20
|
||||
|
||||
# You can configure the interface to send
|
||||
# out identification on the channel with
|
||||
# a set interval by configuring the
|
||||
# following two parameters. The KISS
|
||||
# interface will only ID if the set
|
||||
# interval has elapsed since it's last
|
||||
# actual transmission. The interval is
|
||||
# configured in seconds.
|
||||
# This option is commented out and not
|
||||
# used by default.
|
||||
# id_callsign = MYCALL-0
|
||||
# id_interval = 600
|
||||
|
||||
# Whether to use KISS flow-control.
|
||||
# This is useful for modems that have
|
||||
# a small internal packet buffer, but
|
||||
# support packet flow control instead.
|
||||
flow_control = false
|
||||
|
||||
|
||||
# If you're using Reticulum on amateur radio spectrum,
|
||||
# you might want to use the AX.25 KISS interface. This
|
||||
# way, Reticulum will automatically encapsulate it's
|
||||
# traffic in AX.25 and also identify your stations
|
||||
# transmissions with your callsign and SSID.
|
||||
#
|
||||
# Only do this if you really need to! Reticulum doesn't
|
||||
# need the AX.25 layer for anything, and it incurs extra
|
||||
# overhead on every packet to encapsulate in AX.25.
|
||||
#
|
||||
# A more efficient way is to use the plain KISS interface
|
||||
# with the beaconing functionality described above.
|
||||
|
||||
[[Packet Radio AX.25 KISS Interface]]
|
||||
type = AX25KISSInterface
|
||||
|
||||
# Set the station callsign and SSID
|
||||
callsign = NO1CLL
|
||||
ssid = 0
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB2
|
||||
|
||||
# Set the serial baud-rate and other
|
||||
# configuration parameters.
|
||||
speed = 115200
|
||||
databits = 8
|
||||
parity = none
|
||||
stopbits = 1
|
||||
|
||||
# Whether to use KISS flow-control.
|
||||
# This is useful for modems with a
|
||||
# small internal packet buffer.
|
||||
flow_control = false
|
||||
|
||||
# Set the modem preamble. A 150ms
|
||||
# preamble should be a reasonable
|
||||
# default, but may need to be
|
||||
# increased for radios with slow-
|
||||
# opening squelch and long TX/RX
|
||||
# turnaround
|
||||
preamble = 150
|
||||
|
||||
# Set the modem TX tail. In most
|
||||
# cases this should be kept as low
|
||||
# as possible to not waste airtime.
|
||||
txtail = 10
|
||||
|
||||
# Configure CDMA parameters. These
|
||||
# settings are reasonable defaults.
|
||||
persistence = 200
|
||||
slottime = 20
|
||||
|
||||
'''
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import argparse
|
||||
|
||||
@@ -26,34 +48,86 @@ def size_str(num, suffix='B'):
|
||||
|
||||
def program_setup(configdir, dispall=False, verbosity = 0):
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
ifstats = reticulum.get_interface_stats()
|
||||
if ifstats != None:
|
||||
for ifstat in ifstats:
|
||||
|
||||
stats = None
|
||||
try:
|
||||
stats = reticulum.get_interface_stats()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if stats != None:
|
||||
for ifstat in stats["interfaces"]:
|
||||
name = ifstat["name"]
|
||||
|
||||
if dispall or not (name.startswith("LocalInterface[") or name.startswith("TCPInterface[Client")):
|
||||
print("")
|
||||
|
||||
if ifstat["status"]:
|
||||
ss = "Up"
|
||||
else:
|
||||
ss = "Down"
|
||||
|
||||
if ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
modestr = "Access Point"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_POINT_TO_POINT:
|
||||
modestr = "Point-to-Point"
|
||||
else:
|
||||
modestr = "Full"
|
||||
|
||||
|
||||
if ifstat["clients"] != None:
|
||||
clients = ifstat["clients"]
|
||||
if name.startswith("Shared Instance["):
|
||||
clients_string = "Connected applications: "+str(max(clients-1,0))
|
||||
cnum = max(clients-1,0)
|
||||
if cnum == 1:
|
||||
spec_str = " program"
|
||||
else:
|
||||
spec_str = " programs"
|
||||
|
||||
clients_string = "Serving : "+str(cnum)+spec_str
|
||||
else:
|
||||
clients_string = "Connected clients: "+str(clients)
|
||||
clients_string = "Clients : "+str(clients)
|
||||
|
||||
else:
|
||||
clients = None
|
||||
|
||||
print(" {n}".format(n=ifstat["name"]))
|
||||
print("\tStatus: {ss}".format(ss=ss))
|
||||
|
||||
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
||||
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
||||
|
||||
print(" Status : {ss}".format(ss=ss))
|
||||
|
||||
if clients != None:
|
||||
print("\t"+clients_string)
|
||||
print("\tRX: {rxb}\n\tTX: {txb}".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
||||
print(" "+clients_string)
|
||||
|
||||
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
||||
print(" Mode : {mode}".format(mode=modestr))
|
||||
|
||||
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||
|
||||
if "peers" in ifstat and ifstat["peers"] != None:
|
||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||
|
||||
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))
|
||||
|
||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||
|
||||
if "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
||||
aqn = ifstat["announce_queue"]
|
||||
if aqn == 1:
|
||||
print(" Queued : {np} announce".format(np=aqn))
|
||||
else:
|
||||
print(" Queued : {np} announces".format(np=aqn))
|
||||
|
||||
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
||||
|
||||
if "transport_id" in stats and stats["transport_id"] != None:
|
||||
print("\n Reticulum Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
|
||||
|
||||
print("")
|
||||
|
||||
@@ -89,5 +163,21 @@ def main():
|
||||
print("")
|
||||
exit()
|
||||
|
||||
def speed_str(num, suffix='bps'):
|
||||
units = ['','k','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
|
||||
if suffix == 'Bps':
|
||||
num /= 8
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
|
||||
for unit in units:
|
||||
if abs(num) < 1000.0:
|
||||
return "%3.2f %s%s" % (num, unit, suffix)
|
||||
num /= 1000.0
|
||||
|
||||
return "%.2f %s%s" % (num, last_unit, suffix)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+35
-4
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
@@ -38,7 +60,8 @@ logfile = None
|
||||
logdest = LOG_STDOUT
|
||||
logtimefmt = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
random.seed(os.urandom(10))
|
||||
instance_random = random.Random()
|
||||
instance_random.seed(os.urandom(10))
|
||||
|
||||
_always_override_destination = False
|
||||
|
||||
@@ -71,12 +94,15 @@ def host_os():
|
||||
from .vendor.platformutils import get_platform
|
||||
return get_platform()
|
||||
|
||||
def timestamp_str(time_s):
|
||||
timestamp = time.localtime(time_s)
|
||||
return time.strftime(logtimefmt, timestamp)
|
||||
|
||||
def log(msg, level=3, _override_destination = False):
|
||||
global _always_override_destination
|
||||
|
||||
if loglevel >= level:
|
||||
timestamp = time.time()
|
||||
logstring = "["+time.strftime(logtimefmt)+"] ["+loglevelname(level)+"] "+msg
|
||||
logstring = "["+timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg
|
||||
logging_lock.acquire()
|
||||
|
||||
if (logdest == LOG_STDOUT or _always_override_destination or _override_destination):
|
||||
@@ -105,10 +131,15 @@ def log(msg, level=3, _override_destination = False):
|
||||
|
||||
|
||||
def rand():
|
||||
result = random.random()
|
||||
result = instance_random.random()
|
||||
return result
|
||||
|
||||
def hexrep(data, delimit=True):
|
||||
try:
|
||||
iter(data)
|
||||
except TypeError:
|
||||
data = [data]
|
||||
|
||||
delimiter = ":"
|
||||
if not delimit:
|
||||
delimiter = ""
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "0.3.0"
|
||||
__version__ = "0.3.5"
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2018 Viktor Villainov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
A modern asynchronous library for building I2P applications.
|
||||
"""
|
||||
|
||||
from .__version__ import (
|
||||
__title__, __description__, __url__, __version__,
|
||||
__author__, __author_email__, __license__, __copyright__
|
||||
)
|
||||
|
||||
from .sam import Destination, PrivateKey
|
||||
|
||||
from .aiosam import (
|
||||
get_sam_socket, dest_lookup, new_destination,
|
||||
create_session, stream_connect, stream_accept,
|
||||
Session, StreamConnection, StreamAcceptor
|
||||
)
|
||||
|
||||
from .tunnel import ClientTunnel, ServerTunnel
|
||||
|
||||
from .utils import get_sam_address
|
||||
|
||||
from .exceptions import (
|
||||
CantReachPeer, DuplicatedDest, DuplicatedId, I2PError,
|
||||
InvalidId, InvalidKey, KeyNotFound, PeerNotFound, Timeout,
|
||||
)
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
__title__ = 'i2plib'
|
||||
__description__ = 'A modern asynchronous library for building I2P applications.'
|
||||
__url__ = 'https://github.com/l-n-s/i2plib'
|
||||
__version__ = '0.0.14'
|
||||
__author__ = 'Viktor Villainov'
|
||||
__author_email__ = 'supervillain@riseup.net'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2018 Viktor Villainov'
|
||||
Vendored
+258
@@ -0,0 +1,258 @@
|
||||
import asyncio
|
||||
|
||||
from . import sam
|
||||
from . import exceptions
|
||||
from . import utils
|
||||
from .log import logger
|
||||
|
||||
def parse_reply(data):
|
||||
if not data:
|
||||
raise ConnectionAbortedError("Empty response: SAM API went offline")
|
||||
|
||||
try:
|
||||
msg = sam.Message(data.decode().strip())
|
||||
logger.debug("SAM reply: "+str(msg))
|
||||
except:
|
||||
raise ConnectionAbortedError("Invalid SAM response")
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
async def get_sam_socket(sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||
"""A couroutine used to create a new SAM socket.
|
||||
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) event loop instance
|
||||
:return: A (reader, writer) pair
|
||||
"""
|
||||
reader, writer = await asyncio.open_connection(*sam_address, loop=loop)
|
||||
writer.write(sam.hello("3.1", "3.1"))
|
||||
reply = parse_reply(await reader.readline())
|
||||
if reply.ok:
|
||||
return (reader, writer)
|
||||
else:
|
||||
writer.close()
|
||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
||||
async def dest_lookup(domain, sam_address=sam.DEFAULT_ADDRESS,
|
||||
loop=None):
|
||||
"""A coroutine used to lookup a full I2P destination by .i2p domain or
|
||||
.b32.i2p address.
|
||||
|
||||
:param domain: Address to be resolved, can be a .i2p domain or a .b32.i2p
|
||||
address.
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:return: An instance of :class:`Destination`
|
||||
"""
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(sam.naming_lookup(domain))
|
||||
reply = parse_reply(await reader.readline())
|
||||
writer.close()
|
||||
if reply.ok:
|
||||
return sam.Destination(reply["VALUE"])
|
||||
else:
|
||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
||||
async def new_destination(sam_address=sam.DEFAULT_ADDRESS, loop=None,
|
||||
sig_type=sam.Destination.default_sig_type):
|
||||
"""A coroutine used to generate a new destination with a private key of a
|
||||
chosen signature type.
|
||||
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:param sig_type: (optional) Signature type
|
||||
:return: An instance of :class:`Destination`
|
||||
"""
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(sam.dest_generate(sig_type))
|
||||
reply = parse_reply(await reader.readline())
|
||||
writer.close()
|
||||
return sam.Destination(reply["PRIV"], has_private_key=True)
|
||||
|
||||
async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||
loop=None, style="STREAM",
|
||||
signature_type=sam.Destination.default_sig_type,
|
||||
destination=None, options={}):
|
||||
"""A coroutine used to create a new SAM session.
|
||||
|
||||
:param session_name: Session nick name
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
||||
:param signature_type: (optional) If the destination is TRANSIENT, this
|
||||
signature type is used
|
||||
:param destination: (optional) Destination to use in this session. Can be
|
||||
a base64 encoded string, :class:`Destination`
|
||||
instance or None. TRANSIENT destination is used when it
|
||||
is None.
|
||||
:param options: (optional) A dict object with i2cp options
|
||||
:return: A (reader, writer) pair
|
||||
"""
|
||||
logger.debug("Creating session {}".format(session_name))
|
||||
if destination:
|
||||
if type(destination) == sam.Destination:
|
||||
destination = destination
|
||||
else:
|
||||
destination = sam.Destination(
|
||||
destination, has_private_key=True)
|
||||
|
||||
dest_string = destination.private_key.base64
|
||||
else:
|
||||
dest_string = sam.TRANSIENT_DESTINATION
|
||||
|
||||
options = " ".join(["{}={}".format(k, v) for k, v in options.items()])
|
||||
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(sam.session_create(
|
||||
style, session_name, dest_string, options))
|
||||
|
||||
reply = parse_reply(await reader.readline())
|
||||
if reply.ok:
|
||||
if not destination:
|
||||
destination = sam.Destination(
|
||||
reply["DESTINATION"], has_private_key=True)
|
||||
logger.debug(destination.base32)
|
||||
logger.debug("Session created {}".format(session_name))
|
||||
return (reader, writer)
|
||||
else:
|
||||
writer.close()
|
||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
||||
async def stream_connect(session_name, destination,
|
||||
sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||
"""A coroutine used to connect to a remote I2P destination.
|
||||
|
||||
:param session_name: Session nick name
|
||||
:param destination: I2P destination to connect to
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:return: A (reader, writer) pair
|
||||
"""
|
||||
logger.debug("Connecting stream {}".format(session_name))
|
||||
if isinstance(destination, str) and not destination.endswith(".i2p"):
|
||||
destination = sam.Destination(destination)
|
||||
elif isinstance(destination, str):
|
||||
destination = await dest_lookup(destination, sam_address, loop)
|
||||
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(sam.stream_connect(session_name, destination.base64,
|
||||
silent="false"))
|
||||
reply = parse_reply(await reader.readline())
|
||||
if reply.ok:
|
||||
logger.debug("Stream connected {}".format(session_name))
|
||||
return (reader, writer)
|
||||
else:
|
||||
writer.close()
|
||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
||||
async def stream_accept(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||
loop=None):
|
||||
"""A coroutine used to accept a connection from the I2P network.
|
||||
|
||||
:param session_name: Session nick name
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:return: A (reader, writer) pair
|
||||
"""
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(sam.stream_accept(session_name, silent="false"))
|
||||
reply = parse_reply(await reader.readline())
|
||||
if reply.ok:
|
||||
return (reader, writer)
|
||||
else:
|
||||
writer.close()
|
||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
||||
### Context managers
|
||||
|
||||
class Session:
|
||||
"""Async SAM session context manager.
|
||||
|
||||
:param session_name: Session nick name
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
||||
:param signature_type: (optional) If the destination is TRANSIENT, this
|
||||
signature type is used
|
||||
:param destination: (optional) Destination to use in this session. Can be
|
||||
a base64 encoded string, :class:`Destination`
|
||||
instance or None. TRANSIENT destination is used when it
|
||||
is None.
|
||||
:param options: (optional) A dict object with i2cp options
|
||||
:return: :class:`Session` object
|
||||
"""
|
||||
def __init__(self, session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||
loop=None, style="STREAM",
|
||||
signature_type=sam.Destination.default_sig_type,
|
||||
destination=None, options={}):
|
||||
self.session_name = session_name
|
||||
self.sam_address = sam_address
|
||||
self.loop = loop
|
||||
self.style = style
|
||||
self.signature_type = signature_type
|
||||
self.destination = destination
|
||||
self.options = options
|
||||
|
||||
async def __aenter__(self):
|
||||
self.reader, self.writer = await create_session(self.session_name,
|
||||
sam_address=self.sam_address, loop=self.loop, style=self.style,
|
||||
signature_type=self.signature_type,
|
||||
destination=self.destination, options=self.options)
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
### TODO handle exceptions
|
||||
self.writer.close()
|
||||
|
||||
class StreamConnection:
|
||||
"""Async stream connection context manager.
|
||||
|
||||
:param session_name: Session nick name
|
||||
:param destination: I2P destination to connect to
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:return: :class:`StreamConnection` object
|
||||
"""
|
||||
def __init__(self, session_name, destination,
|
||||
sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||
self.session_name = session_name
|
||||
self.sam_address = sam_address
|
||||
self.loop = loop
|
||||
self.destination = destination
|
||||
|
||||
async def __aenter__(self):
|
||||
self.reader, self.writer = await stream_connect(self.session_name,
|
||||
self.destination, sam_address=self.sam_address, loop=self.loop)
|
||||
self.read = self.reader.read
|
||||
self.write = self.writer.write
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
### TODO handle exceptions
|
||||
self.writer.close()
|
||||
|
||||
class StreamAcceptor:
|
||||
"""Async stream acceptor context manager.
|
||||
|
||||
:param session_name: Session nick name
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:return: :class:`StreamAcceptor` object
|
||||
"""
|
||||
def __init__(self, session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||
loop=None):
|
||||
self.session_name = session_name
|
||||
self.sam_address = sam_address
|
||||
self.loop = loop
|
||||
|
||||
async def __aenter__(self):
|
||||
self.reader, self.writer = await stream_accept(self.session_name,
|
||||
sam_address=self.sam_address, loop=self.loop)
|
||||
self.read = self.reader.read
|
||||
self.write = self.writer.write
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
### TODO handle exceptions
|
||||
self.writer.close()
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
# SAM exceptions
|
||||
|
||||
class SAMException(IOError):
|
||||
"""Base class for SAM exceptions"""
|
||||
|
||||
class CantReachPeer(SAMException):
|
||||
"""The peer exists, but cannot be reached"""
|
||||
|
||||
class DuplicatedDest(SAMException):
|
||||
"""The specified Destination is already in use"""
|
||||
|
||||
class DuplicatedId(SAMException):
|
||||
"""The nickname is already associated with a session"""
|
||||
|
||||
class I2PError(SAMException):
|
||||
"""A generic I2P error"""
|
||||
|
||||
class InvalidId(SAMException):
|
||||
"""STREAM SESSION ID doesn't exist"""
|
||||
|
||||
class InvalidKey(SAMException):
|
||||
"""The specified key is not valid (bad format, etc.)"""
|
||||
|
||||
class KeyNotFound(SAMException):
|
||||
"""The naming system can't resolve the given name"""
|
||||
|
||||
class PeerNotFound(SAMException):
|
||||
"""The peer cannot be found on the network"""
|
||||
|
||||
class Timeout(SAMException):
|
||||
"""The peer cannot be found on the network"""
|
||||
|
||||
SAM_EXCEPTIONS = {
|
||||
"CANT_REACH_PEER": CantReachPeer,
|
||||
"DUPLICATED_DEST": DuplicatedDest,
|
||||
"DUPLICATED_ID": DuplicatedId,
|
||||
"I2P_ERROR": I2PError,
|
||||
"INVALID_ID": InvalidId,
|
||||
"INVALID_KEY": InvalidKey,
|
||||
"KEY_NOT_FOUND": KeyNotFound,
|
||||
"PEER_NOT_FOUND": PeerNotFound,
|
||||
"TIMEOUT": Timeout,
|
||||
}
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
"""Logging configuration."""
|
||||
import logging
|
||||
|
||||
# Name the logger after the package.
|
||||
logger = logging.getLogger(__package__)
|
||||
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
from base64 import b64decode, b64encode, b32encode
|
||||
from hashlib import sha256
|
||||
import struct
|
||||
import re
|
||||
|
||||
|
||||
I2P_B64_CHARS = "-~"
|
||||
|
||||
def i2p_b64encode(x):
|
||||
"""Encode I2P destination"""
|
||||
return b64encode(x, altchars=I2P_B64_CHARS.encode()).decode()
|
||||
|
||||
def i2p_b64decode(x):
|
||||
"""Decode I2P destination"""
|
||||
return b64decode(x, altchars=I2P_B64_CHARS, validate=True)
|
||||
|
||||
SAM_BUFSIZE = 4096
|
||||
DEFAULT_ADDRESS = ("127.0.0.1", 7656)
|
||||
DEFAULT_MIN_VER = "3.1"
|
||||
DEFAULT_MAX_VER = "3.1"
|
||||
TRANSIENT_DESTINATION = "TRANSIENT"
|
||||
|
||||
VALID_BASE32_ADDRESS = re.compile(r"^([a-zA-Z0-9]{52}).b32.i2p$")
|
||||
VALID_BASE64_ADDRESS = re.compile(r"^([a-zA-Z0-9-~=]{516,528})$")
|
||||
|
||||
class Message(object):
|
||||
"""Parse SAM message to an object"""
|
||||
def __init__(self, s):
|
||||
self.opts = {}
|
||||
if type(s) != str:
|
||||
self._reply_string = s.decode().strip()
|
||||
else:
|
||||
self._reply_string = s
|
||||
|
||||
self.cmd, self.action, opts = self._reply_string.split(" ", 2)
|
||||
for v in opts.split(" "):
|
||||
data = v.split("=", 1) if "=" in v else (v, True)
|
||||
self.opts[data[0]] = data[1]
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.opts[key]
|
||||
|
||||
@property
|
||||
def ok(self):
|
||||
return self["RESULT"] == "OK"
|
||||
|
||||
def __repr__(self):
|
||||
return self._reply_string
|
||||
|
||||
|
||||
# SAM request messages
|
||||
|
||||
def hello(min_version, max_version):
|
||||
return "HELLO VERSION MIN={} MAX={}\n".format(min_version,
|
||||
max_version).encode()
|
||||
|
||||
def session_create(style, session_id, destination, options=""):
|
||||
return "SESSION CREATE STYLE={} ID={} DESTINATION={} {}\n".format(
|
||||
style, session_id, destination, options).encode()
|
||||
|
||||
|
||||
def stream_connect(session_id, destination, silent="false"):
|
||||
return "STREAM CONNECT ID={} DESTINATION={} SILENT={}\n".format(
|
||||
session_id, destination, silent).encode()
|
||||
|
||||
def stream_accept(session_id, silent="false"):
|
||||
return "STREAM ACCEPT ID={} SILENT={}\n".format(session_id, silent).encode()
|
||||
|
||||
def stream_forward(session_id, port, options=""):
|
||||
return "STREAM FORWARD ID={} PORT={} {}\n".format(
|
||||
session_id, port, options).encode()
|
||||
|
||||
|
||||
|
||||
def naming_lookup(name):
|
||||
return "NAMING LOOKUP NAME={}\n".format(name).encode()
|
||||
|
||||
def dest_generate(signature_type):
|
||||
return "DEST GENERATE SIGNATURE_TYPE={}\n".format(signature_type).encode()
|
||||
|
||||
class Destination(object):
|
||||
"""I2P destination
|
||||
|
||||
https://geti2p.net/spec/common-structures#destination
|
||||
|
||||
:param data: (optional) Base64 encoded data or binary data
|
||||
:param path: (optional) A path to a file with binary data
|
||||
:param has_private_key: (optional) Does data have a private key?
|
||||
"""
|
||||
|
||||
ECDSA_SHA256_P256 = 1
|
||||
ECDSA_SHA384_P384 = 2
|
||||
ECDSA_SHA512_P521 = 3
|
||||
EdDSA_SHA512_Ed25519 = 7
|
||||
|
||||
default_sig_type = EdDSA_SHA512_Ed25519
|
||||
|
||||
_pubkey_size = 256
|
||||
_signkey_size = 128
|
||||
_min_cert_size = 3
|
||||
|
||||
def __init__(self, data=None, path=None, has_private_key=False):
|
||||
#: Binary destination
|
||||
self.data = bytes()
|
||||
#: Base64 encoded destination
|
||||
self.base64 = ""
|
||||
#: :class:`RNS.vendor.i2plib.PrivateKey` instance or None
|
||||
self.private_key = None
|
||||
|
||||
if path:
|
||||
with open(path, "rb") as f: data = f.read()
|
||||
|
||||
if data and has_private_key:
|
||||
self.private_key = PrivateKey(data)
|
||||
|
||||
cert_len = struct.unpack("!H", self.private_key.data[385:387])[0]
|
||||
data = self.private_key.data[:387+cert_len]
|
||||
|
||||
if not data:
|
||||
raise Exception("Can't create a destination with no data")
|
||||
|
||||
self.data = data if type(data) == bytes else i2p_b64decode(data)
|
||||
self.base64 = data if type(data) == str else i2p_b64encode(data)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Destination: {}>".format(self.base32)
|
||||
|
||||
@property
|
||||
def base32(self):
|
||||
"""Base32 destination hash of this destination"""
|
||||
desthash = sha256(self.data).digest()
|
||||
return b32encode(desthash).decode()[:52].lower()
|
||||
|
||||
class PrivateKey(object):
|
||||
"""I2P private key
|
||||
|
||||
https://geti2p.net/spec/common-structures#keysandcert
|
||||
|
||||
:param data: Base64 encoded data or binary data
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
#: Binary private key
|
||||
self.data = data if type(data) == bytes else i2p_b64decode(data)
|
||||
#: Base64 encoded private key
|
||||
self.base64 = data if type(data) == str else i2p_b64encode(data)
|
||||
|
||||
Vendored
+202
@@ -0,0 +1,202 @@
|
||||
import logging
|
||||
import asyncio
|
||||
import argparse
|
||||
|
||||
from . import sam
|
||||
from . import aiosam
|
||||
from . import utils
|
||||
from .log import logger
|
||||
|
||||
BUFFER_SIZE = 65536
|
||||
|
||||
async def proxy_data(reader, writer):
|
||||
"""Proxy data from reader to writer"""
|
||||
try:
|
||||
while True:
|
||||
data = await reader.read(BUFFER_SIZE)
|
||||
if not data:
|
||||
break
|
||||
writer.write(data)
|
||||
except Exception as e:
|
||||
logger.debug('proxy_data_task exception {}'.format(e))
|
||||
finally:
|
||||
try:
|
||||
writer.close()
|
||||
except RuntimeError:
|
||||
pass
|
||||
logger.debug('close connection')
|
||||
|
||||
class I2PTunnel(object):
|
||||
"""Base I2P Tunnel object, not to be used directly
|
||||
|
||||
:param local_address: A local address to use for a tunnel.
|
||||
E.g. ("127.0.0.1", 6668)
|
||||
:param destination: (optional) Destination to use for this tunnel. Can be
|
||||
a base64 encoded string, :class:`Destination`
|
||||
instance or None. A new destination is created when it
|
||||
is None.
|
||||
:param session_name: (optional) Session nick name. A new session nickname is
|
||||
generated if not specified.
|
||||
:param options: (optional) A dict object with i2cp options
|
||||
:param loop: (optional) Event loop instance
|
||||
:param sam_address: (optional) SAM API address
|
||||
"""
|
||||
|
||||
def __init__(self, local_address, destination=None, session_name=None,
|
||||
options={}, loop=None, sam_address=sam.DEFAULT_ADDRESS):
|
||||
self.local_address = local_address
|
||||
self.destination = destination
|
||||
self.session_name = session_name or utils.generate_session_id()
|
||||
self.options = options
|
||||
self.loop = loop
|
||||
self.sam_address = sam_address
|
||||
|
||||
async def _pre_run(self):
|
||||
if not self.destination:
|
||||
self.destination = await aiosam.new_destination(
|
||||
sam_address=self.sam_address, loop=self.loop)
|
||||
_, self.session_writer = await aiosam.create_session(
|
||||
self.session_name, style=self.style, options=self.options,
|
||||
sam_address=self.sam_address,
|
||||
loop=self.loop, destination=self.destination)
|
||||
|
||||
def stop(self):
|
||||
"""Stop the tunnel"""
|
||||
self.session_writer.close()
|
||||
|
||||
class ClientTunnel(I2PTunnel):
|
||||
"""Client tunnel, a subclass of tunnel.I2PTunnel
|
||||
|
||||
If you run a client tunnel with a local address ("127.0.0.1", 6668) and
|
||||
a remote destination "irc.echelon.i2p", all connections to 127.0.0.1:6668
|
||||
will be proxied to irc.echelon.i2p.
|
||||
|
||||
:param remote_destination: Remote I2P destination, can be either .i2p
|
||||
domain, .b32.i2p address, base64 destination or
|
||||
:class:`Destination` instance
|
||||
"""
|
||||
|
||||
def __init__(self, remote_destination, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.style = "STREAM"
|
||||
self.remote_destination = remote_destination
|
||||
|
||||
async def run(self):
|
||||
"""A coroutine used to run the tunnel"""
|
||||
await self._pre_run()
|
||||
|
||||
async def handle_client(client_reader, client_writer):
|
||||
"""Handle local client connection"""
|
||||
remote_reader, remote_writer = await aiosam.stream_connect(
|
||||
self.session_name, self.remote_destination,
|
||||
sam_address=self.sam_address, loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
||||
loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
||||
loop=self.loop)
|
||||
|
||||
self.server = await asyncio.start_server(handle_client, *self.local_address, loop=self.loop)
|
||||
|
||||
def stop(self):
|
||||
super().stop()
|
||||
self.server.close()
|
||||
|
||||
class ServerTunnel(I2PTunnel):
|
||||
"""Server tunnel, a subclass of tunnel.I2PTunnel
|
||||
|
||||
If you want to expose a local service 127.0.0.1:80 to the I2P network, run
|
||||
a server tunnel with a local address ("127.0.0.1", 80). If you don't
|
||||
provide a private key or a session name, it will use a TRANSIENT
|
||||
destination.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.style = "STREAM"
|
||||
|
||||
async def run(self):
|
||||
"""A coroutine used to run the tunnel"""
|
||||
await self._pre_run()
|
||||
|
||||
async def handle_client(incoming, client_reader, client_writer):
|
||||
# data and dest may come in one chunk
|
||||
dest, data = incoming.split(b"\n", 1)
|
||||
remote_destination = sam.Destination(dest.decode())
|
||||
logger.debug("{} client connected: {}.b32.i2p".format(
|
||||
self.session_name, remote_destination.base32))
|
||||
|
||||
try:
|
||||
remote_reader, remote_writer = await asyncio.wait_for(
|
||||
asyncio.open_connection(
|
||||
host=self.local_address[0],
|
||||
port=self.local_address[1], loop=self.loop),
|
||||
timeout=5, loop=self.loop)
|
||||
if data: remote_writer.write(data)
|
||||
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
||||
loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
||||
loop=self.loop)
|
||||
except ConnectionRefusedError:
|
||||
client_writer.close()
|
||||
|
||||
async def server_loop():
|
||||
try:
|
||||
while True:
|
||||
client_reader, client_writer = await aiosam.stream_accept(
|
||||
self.session_name, sam_address=self.sam_address,
|
||||
loop=self.loop)
|
||||
incoming = await client_reader.read(BUFFER_SIZE)
|
||||
asyncio.ensure_future(handle_client(
|
||||
incoming, client_reader, client_writer), loop=self.loop)
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
self.server_loop = asyncio.ensure_future(server_loop(), loop=self.loop)
|
||||
|
||||
def stop(self):
|
||||
super().stop()
|
||||
self.server_loop.cancel()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('type', metavar="TYPE", choices=('server', 'client'),
|
||||
help="Tunnel type (server or client)")
|
||||
parser.add_argument('address', metavar="ADDRESS",
|
||||
help="Local address (e.g. 127.0.0.1:8000)")
|
||||
parser.add_argument('--debug', '-d', action='store_true',
|
||||
help='Debugging')
|
||||
parser.add_argument('--key', '-k', default='', metavar='PRIVATE_KEY',
|
||||
help='Path to private key file')
|
||||
parser.add_argument('--destination', '-D', default='',
|
||||
metavar='DESTINATION', help='Remote destination')
|
||||
args = parser.parse_args()
|
||||
|
||||
SAM_ADDRESS = utils.get_sam_address()
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.set_debug(args.debug)
|
||||
|
||||
if args.key:
|
||||
destination = sam.Destination(path=args.key, has_private_key=True)
|
||||
else:
|
||||
destination = None
|
||||
|
||||
local_address = utils.address_from_string(args.address)
|
||||
|
||||
if args.type == "client":
|
||||
tunnel = ClientTunnel(args.destination, local_address, loop=loop,
|
||||
destination=destination, sam_address=SAM_ADDRESS)
|
||||
elif args.type == "server":
|
||||
tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
|
||||
sam_address=SAM_ADDRESS)
|
||||
|
||||
asyncio.ensure_future(tunnel.run(), loop=loop)
|
||||
|
||||
try:
|
||||
loop.run_forever()
|
||||
except KeyboardInterrupt:
|
||||
tunnel.stop()
|
||||
finally:
|
||||
loop.stop()
|
||||
loop.close()
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
import socket
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
|
||||
from . import sam
|
||||
|
||||
def get_free_port():
|
||||
"""Get a free port on your local host"""
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', 0))
|
||||
free_port = s.getsockname()[1]
|
||||
s.close()
|
||||
return free_port
|
||||
|
||||
def is_address_accessible(address):
|
||||
"""Check if address is accessible or down"""
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
is_accessible = s.connect_ex(address) == 0
|
||||
s.close()
|
||||
return is_accessible
|
||||
|
||||
def address_from_string(address_string):
|
||||
"""Address tuple from host:port string"""
|
||||
address = address_string.split(":")
|
||||
return (address[0], int(address[1]))
|
||||
|
||||
def get_sam_address():
|
||||
"""
|
||||
Get SAM address from environment variable I2P_SAM_ADDRESS, or use a default
|
||||
value
|
||||
"""
|
||||
value = os.getenv("I2P_SAM_ADDRESS")
|
||||
return address_from_string(value) if value else sam.DEFAULT_ADDRESS
|
||||
|
||||
def generate_session_id(length=6):
|
||||
"""Generate random session id"""
|
||||
rand = random.SystemRandom()
|
||||
sid = [rand.choice(string.ascii_letters) for _ in range(length)]
|
||||
return "reticulum-" + "".join(sid)
|
||||
|
||||
Vendored
+41
-3
@@ -1,7 +1,45 @@
|
||||
def get_platform():
|
||||
from os import environ
|
||||
if 'ANDROID_ARGUMENT' in environ:
|
||||
return 'android'
|
||||
if "ANDROID_ARGUMENT" in environ:
|
||||
return "android"
|
||||
elif "ANDROID_ROOT" in environ:
|
||||
return "android"
|
||||
else:
|
||||
import sys
|
||||
return sys.platform
|
||||
return sys.platform
|
||||
|
||||
def is_darwin():
|
||||
if get_platform() == "darwin":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_android():
|
||||
if get_platform() == "android":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_windows():
|
||||
if str(get_platform()).startswith("win"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def platform_checks():
|
||||
if is_windows():
|
||||
import sys
|
||||
if sys.version_info.major >= 3 and sys.version_info.minor >= 8:
|
||||
pass
|
||||
else:
|
||||
import RNS
|
||||
RNS.log("On Windows, Reticulum requires Python 3.8 or higher.", RNS.LOG_ERROR)
|
||||
RNS.log("Please update Python to run Reticulum.", RNS.LOG_ERROR)
|
||||
RNS.panic()
|
||||
|
||||
def cryptography_old_api():
|
||||
import cryptography
|
||||
if cryptography.__version__ == "2.8":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
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: 373c2d4526d24456ccd5dac65661415a
|
||||
config: d4939f555bda9c488f47cdcede85949d
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
@@ -6,11 +6,31 @@ The best way to get started with the Reticulum Network Stack depends on what
|
||||
you want to do. This guide will outline sensible starting paths for different
|
||||
scenarios.
|
||||
|
||||
|
||||
Try Using a Reticulum-based Program
|
||||
=============================================
|
||||
If you simply want to try using a program built with Reticulum, you can take
|
||||
a look at `Nomad Network <https://github.com/markqvist/nomadnet>`_, which
|
||||
provides a complete encrypted communications suite built with Reticulum.
|
||||
|
||||
If you simply want to try using a program built with Reticulum, a few different
|
||||
programs exist that allow basic communication and a range of other useful functions
|
||||
over even extremely low-bandwidth Reticulum networks.
|
||||
|
||||
These programs will let you get a feel for how Reticulum works. They have been designed
|
||||
to run well over networks based on LoRa or packet radio, but can also be used completely
|
||||
over local WiFi, wired ethernet, the Internet, or any combination.
|
||||
|
||||
As such, it is easy to get started experimenting, without having to set up any radio
|
||||
transceivers or infrastructure just to try it out. Launching the programs on separate
|
||||
devices connected to the same WiFi network is enough to get started, and physical
|
||||
radio interfaces can then be added later.
|
||||
|
||||
Nomad Network
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The terminal-based program `Nomad Network <https://github.com/markqvist/nomadnet>`_
|
||||
provides a complete encrypted communications suite built with Reticulum. It features
|
||||
encrypted messaging (both direct and delayed-delivery for offline users), file sharing,
|
||||
and has a built-in text-browser and page server with support for dynamically rendered pages,
|
||||
user authentication and more.
|
||||
|
||||
.. image:: screenshots/nomadnet_3.png
|
||||
:target: _images/nomadnet_3.png
|
||||
@@ -29,7 +49,25 @@ You can install Nomad Network via pip:
|
||||
# ... and run
|
||||
nomadnet
|
||||
|
||||
**Please Note**: If this is the very first time you use pip to install a program
|
||||
on your system, you might need to reboot your system for your program to become
|
||||
available. If you get a "command not found" error or similar when running the
|
||||
program, reboot your system and try again.
|
||||
|
||||
Sideband
|
||||
^^^^^^^^
|
||||
|
||||
If you would rather use a program with a graphical user interface, you can take
|
||||
a look at `Sideband <https://unsigned.io/sideband>`_, which is available for Android,
|
||||
Linux and macOS.
|
||||
|
||||
.. image:: screenshots/sideband_1.png
|
||||
:width: 400px
|
||||
:align: center
|
||||
:target: _images/sideband_1.png
|
||||
|
||||
Sideband is currently in the early stages of development, but already provides basic
|
||||
communication features, and interoperates with Nomad Network, or any other LXMF client.
|
||||
|
||||
Using the Included Utilities
|
||||
=============================================
|
||||
@@ -44,6 +82,7 @@ network status and connectivity.
|
||||
To learn more about these utility programs, have a look at the
|
||||
:ref:`Using Reticulum on Your System<using-main>` chapter of this manual.
|
||||
|
||||
|
||||
Creating a Network With Reticulum
|
||||
=============================================
|
||||
To create a network, you will need to specify one or more *interfaces* for
|
||||
@@ -53,17 +92,93 @@ or use the interactive ``rnsconfig`` utility.
|
||||
|
||||
When Reticulum is started for the first time, it will create a default
|
||||
configuration file, with one active interface. This default interface uses
|
||||
your existing ethernet network (if there is one), and only allows you to
|
||||
communicate with other Reticulum peers within your local broadcast domain.
|
||||
your existing ethernet and WiFi networks (if any), and only allows you to
|
||||
communicate with other Reticulum peers within your local broadcast domains.
|
||||
|
||||
To communicate further, you will have to add one or more interfaces. The default
|
||||
configuration includes a number of examples, ranging from using TCP over the
|
||||
internet, to LoRa and Packet Radio interfaces.
|
||||
|
||||
With Reticulum, you only need to configure what interfaces you want to communicate
|
||||
over. There is no need to configure address spaces, subnets, routing tables,
|
||||
or other things you might be used to from other network types.
|
||||
|
||||
Once Reticulums knows which interfaces it should use, it will automatically
|
||||
discover topography and configure transport of data to any destinations it
|
||||
knows about.
|
||||
|
||||
In situations where you already have an established WiFi or ethernet network, and
|
||||
many devices that want to utilise the same external Reticulum network (for example over
|
||||
LoRa), it will often be sufficient to let one system act as a Reticulum gateway, by
|
||||
adding any external interfaces to this systems configuration, and enabling transport. Any
|
||||
other device on your local WiFi will then be able to connect to this wider Reticulum
|
||||
network just using the default interface configuration.
|
||||
|
||||
Possibly, the examples in the config file are enough to get you started. If
|
||||
you want more information, you can read the :ref:`Building Networks<networks-main>`
|
||||
and :ref:`Interfaces<interfaces-main>` chapters of this manual.
|
||||
|
||||
Connecting Reticulum Instances Over the Internet
|
||||
================================================
|
||||
Reticulum currently offers two interfaces suitable for connecting instances over the Internet: :ref:`TCP<interfaces-tcps>`
|
||||
and :ref:`I2P<interfaces-i2p>`. Each interface offers a different set of features, and Reticulum
|
||||
users should carefully choose the interface which best suites their needs.
|
||||
|
||||
The ``TCPServerInterface`` allows users to host an instance accessible over TCP/IP. This
|
||||
method is generally faster, lower latency, and more energy efficient than using ``I2PInterface``,
|
||||
however it also leaks more data about the server host.
|
||||
|
||||
TCP connections reveal the IP address of both your instance and the server to anyone who can
|
||||
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
|
||||
inspecting your packets may be able to record packet metadata like time of transmission and packet size.
|
||||
Even though Reticulum encrypts traffic, TCP does not, so an adversary may be able to use
|
||||
packet inspection to learn that a system is running Reticulum, and what other IP adresses connect to it.
|
||||
Hosting a publicly reachable instance over TCP also requires a publicly reachable IP address,
|
||||
which most Internet connections don't offer anymore.
|
||||
|
||||
The ``I2PInterface`` routes messages through the `Invisible Internet Protocol
|
||||
(I2P) <https://geti2p.net/en/>`_. To properly use this interface, users must also run an I2P daemon in
|
||||
parallel to ``rnsd``. For always-on I2P nodes it is recommended to use `i2pd <https://i2pd.website/>`_.
|
||||
|
||||
By default, I2P will encrypt and mix all traffic sent over the Internet, and
|
||||
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
|
||||
will also relay other I2P user's encrypted packets, which will use extra
|
||||
bandwidth and compute power, but also makes timing attacks and other forms of
|
||||
deep-packet-inspection much more difficult.
|
||||
|
||||
I2P also allows users to host globally available Reticulum instances from non-public IPs and behind firewalls.
|
||||
|
||||
In general it is recommended to use an I2P node if you want to host a publically accessible
|
||||
instance, while preserving anonymity. If you care more about performance, and a slightly
|
||||
easier setup, use TCP.
|
||||
|
||||
Connect to the Public Testnet
|
||||
===========================================
|
||||
|
||||
An experimental public testnet has been made accessible over both I2P and TCP. You can join it
|
||||
by adding one of the following interfaces to your ``.reticulum/config`` file:
|
||||
|
||||
.. code::
|
||||
|
||||
# For connecting over TCP/IP:
|
||||
[[RNS Testnet Frankfurt]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = yes
|
||||
outgoing = True
|
||||
target_host = frankfurt.rns.unsigned.io
|
||||
target_port = 4965
|
||||
|
||||
|
||||
# For connecting over I2P:
|
||||
[[RNS Testnet I2P Node A]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
peers = ykzlw5ujbaqc2xkec4cpvgyxj257wcrmmgkuxqmqcur7cq3w3lha.b32.i2p
|
||||
|
||||
Many other Reticulum instances are connecting to this testnet, and you can also join it
|
||||
via other entry points if you know them. There is absolutely no control over the network
|
||||
topography, usage or what types of instances connect. It will also occasionally be used
|
||||
to test various failure scenarios, and there are no availability or service guarantees.
|
||||
|
||||
Develop a Program with Reticulum
|
||||
===========================================
|
||||
@@ -150,8 +265,11 @@ installing Reticulum or programs that depend on Reticulum.
|
||||
Reticulum on Android
|
||||
==============================================
|
||||
Reticulum can be used on Android in different ways. The easiest way to get
|
||||
started is using the `Termux app <https://termux.com/>`_, at the time of writing
|
||||
available on `F-droid <https://f-droid.org>`_.
|
||||
started is using an app like `Sideband <https://unsigned.io/sideband>`_.
|
||||
|
||||
For more control and features, you can use Reticulum and related programs via
|
||||
the `Termux app <https://termux.com/>`_, at the time of writing available on
|
||||
`F-droid <https://f-droid.org>`_.
|
||||
|
||||
Termux is a terminal emulator and Linux environment for Android based devices,
|
||||
which includes the ability to use many different programs and libraries,
|
||||
@@ -191,3 +309,22 @@ From within Termux, execute the following:
|
||||
It is also possible to include Reticulum in apps compiled and distributed as
|
||||
Android APKs. A detailed tutorial and example source code will be included
|
||||
here at a later point.
|
||||
|
||||
Adding Radio Interfaces
|
||||
==============================================
|
||||
Once you have Reticulum installed and working, you can add radio interfaces with
|
||||
any compatible hardware you have available. For information on how to configure
|
||||
this, see the :ref:`Interfaces<interfaces-main>` section of this manual.
|
||||
|
||||
A range of common LoRa development boards and transceiver modules can be used
|
||||
as interfaces with Reticulum. You can refer to the following external resources
|
||||
for more information:
|
||||
|
||||
* `How To Make Your Own RNodes <https://unsigned.io/how-to-make-your-own-rnodes/>`_
|
||||
* `Installing RNode Firmware on Compatible LoRa Devices <https://unsigned.io/installing-rnode-firmware-on-t-beam-and-lora32-devices/>`_
|
||||
* `Private, Secure and Uncensorable Messaging Over a LoRa Mesh <https://unsigned.io/private-messaging-over-lora/>`_
|
||||
* `RNode Firmware <https://github.com/markqvist/RNode_Firmware/>`_
|
||||
|
||||
If you have communications hardware that you think would be suitable for use with Reticulum,
|
||||
you are welcome to head over to the `GitHub discussion pages <https://github.com/markqvist/Reticulum/discussions>`_
|
||||
and propose adding an interface for the hardware.
|
||||
@@ -18,6 +18,72 @@ For a high-level overview of how networks can be formed over different interface
|
||||
types, have a look at the :ref:`Building Networks<networks-main>` chapter of this
|
||||
manual.
|
||||
|
||||
|
||||
.. _interfaces-options:
|
||||
|
||||
Common Interface Options
|
||||
========================
|
||||
|
||||
A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.
|
||||
|
||||
|
||||
* | The ``enabled`` option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to ``False``. For any
|
||||
interface to be brought up, the ``enabled`` option
|
||||
must be set to ``True`` or ``Yes``.
|
||||
|
||||
* | The ``mode`` option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.
|
||||
|
||||
- The default value is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is available.
|
||||
|
||||
- In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.
|
||||
|
||||
* | The ``outgoing`` option sets whether an interface is allowed
|
||||
to transmit. Defaults to ``True``. If set to ``False`` or ``No``
|
||||
the interface will only receive data, and never transmit.
|
||||
|
||||
* | The ``network_name`` option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.
|
||||
|
||||
* | The ``passphrase`` option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
``network_name`` option, or be used alone.
|
||||
|
||||
* | The ``ifac_size`` option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.
|
||||
|
||||
* | The ``announce_cap`` option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between ``1`` and ``100``.
|
||||
|
||||
* | The ``bitrate`` option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn't report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the ``bitrate``
|
||||
option, to set the interface speed in *bits per second*.
|
||||
|
||||
|
||||
.. _interfaces-auto:
|
||||
|
||||
Auto Interface
|
||||
@@ -39,7 +105,6 @@ system, which should be enabled by default in almost all OSes.
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# You can create multiple isolated Reticulum
|
||||
# networks on the same physical LAN by
|
||||
@@ -69,7 +134,6 @@ the discovery scope by setting it to one of ``link``, ``admin``, ``site``,
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# Configure global discovery
|
||||
|
||||
@@ -81,8 +145,175 @@ the discovery scope by setting it to one of ``link``, ``admin``, ``site``,
|
||||
discovery_port = 48555
|
||||
data_port = 49555
|
||||
|
||||
*Please Note!* If you use the Auto Interface, you will need the Python module
|
||||
``netifaces`` installed on your system. You can install it with ``pip3 install netifaces``.
|
||||
|
||||
.. _interfaces-i2p:
|
||||
|
||||
I2P Interface
|
||||
=============
|
||||
|
||||
The I2P interface lets you connect Reticulum instances over the
|
||||
`Invisible Internet Protocol <https://i2pd.website>`_. This can be
|
||||
especially useful in cases where you want to host a globally reachable
|
||||
Reticulum instance, but do not have access to any public IP addresses,
|
||||
have a frequently changing IP address, or have firewalls blocking
|
||||
inbound traffic.
|
||||
|
||||
Using the I2P interface, you will get a globally reachable, portable
|
||||
and persistent I2P address that your Reticulum instance can be reached
|
||||
at.
|
||||
|
||||
To use the I2P interface, you must have an I2P router running
|
||||
on your system. The easiest way to acheive this is to download and
|
||||
install the `latest release <https://github.com/PurpleI2P/i2pd/releases/latest>`_
|
||||
of the ``ì2pd`` package. For more details about I2P, see the
|
||||
`geti2p.net website <https://geti2p.net/en/about/intro>`_.`
|
||||
|
||||
When an I2P router is running on your system, you can simply add
|
||||
an I2P interface to reticulum:
|
||||
|
||||
.. code::
|
||||
|
||||
[[I2P]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
connectable = yes
|
||||
|
||||
On the first start, Reticulum will generate a new I2P address for the
|
||||
interface and start listening for inbound traffic on it. This can take
|
||||
a while the first time, especially if your I2P router was also just
|
||||
started, and is not yet well-connected to the I2P network. When ready,
|
||||
you should see I2P base32 address printed to your log file. You can
|
||||
also inspect the status of the interface using the ``rnstatus`` utility.
|
||||
|
||||
To connect to other Reticulum instances over I2P, just add a comma-separated
|
||||
list of I2P base32 addresses to the ``peers`` option of the interface:
|
||||
|
||||
.. code::
|
||||
|
||||
[[I2P]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
connectable = yes
|
||||
peers = 5urvjicpzi7q3ybztsef4i5ow2aq4soktfj7zedz53s47r54jnqq.b32.i2p
|
||||
|
||||
It can take anywhere from a few seconds to a few minutes to establish
|
||||
I2P connections to the desired peers, so Reticulum handles the process
|
||||
in the background, and will output relevant events to the log.
|
||||
|
||||
**Please Note!** While the I2P interface is the simplest way to use
|
||||
Reticulum over I2P, it is also possible to tunnel the TCP server and
|
||||
client interfaces over I2P manually. This can be useful in situations
|
||||
where more control is needed, but requires manual tunnel setup through
|
||||
the I2P daemon configuration.
|
||||
|
||||
It is important to note that the two methods are *interchangably compatible*.
|
||||
You can use the I2PInterface to connect to a TCPServerInterface that
|
||||
was manually tunneled over I2P, for example. This offers a high degree
|
||||
of flexibility in network setup, while retaining ease of use in simpler
|
||||
use-cases.
|
||||
|
||||
|
||||
.. _interfaces-tcps:
|
||||
|
||||
TCP Server Interface
|
||||
====================
|
||||
|
||||
The TCP Server interface is suitable for allowing other peers to connect over
|
||||
the Internet or private IP networks. When a TCP server interface has been
|
||||
configured, other Reticulum peers can connect to it with a TCP Client interface.
|
||||
|
||||
.. code::
|
||||
|
||||
# This example demonstrates a TCP server interface.
|
||||
# It will listen for incoming connections on the
|
||||
# specified IP address and port number.
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = True
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
|
||||
# Alternatively you can bind to a specific IP
|
||||
|
||||
# listen_ip = 10.0.0.88
|
||||
# listen_port = 4242
|
||||
|
||||
# Or a specific network device
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
**Please Note!** The TCP interfaces support tunneling over I2P, but to do so reliably,
|
||||
you must use the i2p_tunneled option:
|
||||
|
||||
.. code::
|
||||
|
||||
[[TCP Server on I2P]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = yes
|
||||
listen_ip = 127.0.0.1
|
||||
listen_port = 5001
|
||||
i2p_tunneled = yes
|
||||
|
||||
.. _interfaces-tcpc:
|
||||
|
||||
TCP Client Interface
|
||||
====================
|
||||
|
||||
To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface. The
|
||||
# target_host can either be an IP address or a hostname.
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
It is also possible to use this interface type to connect via other programs
|
||||
or hardware devices that expose a KISS interface on a TCP port, for example
|
||||
software-based soundmodems. To do this, use the ``kiss_framing`` option:
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface that connects
|
||||
# to a software TNC soundmodem on a KISS over TCP port.
|
||||
|
||||
[[TCP KISS Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
kiss_framing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 8001
|
||||
|
||||
**Caution!** Only use the KISS framing option when connecting to external devices
|
||||
and programs like soundmodems and similar over TCP. When using the
|
||||
``TCPClientInterface`` in conjunction with the ``TCPServerInterface`` you should
|
||||
never enable ``kiss_framing``, since this will disable internal reliability and
|
||||
recovery mechanisms that greatly improves performance over unreliable and
|
||||
intermittent TCP links.
|
||||
|
||||
**Please Note!** The TCP interfaces support tunneling over I2P, but to do so reliably,
|
||||
you must use the i2p_tunneled option:
|
||||
|
||||
.. code::
|
||||
|
||||
[[TCP Client over I2P]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = yes
|
||||
target_host = 127.0.0.1
|
||||
target_port = 5001
|
||||
i2p_tunneled = yes
|
||||
|
||||
|
||||
.. _interfaces-udp:
|
||||
@@ -113,7 +344,7 @@ pre-existing LAN.
|
||||
[[Default UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
forward_ip = 255.255.255.255
|
||||
@@ -149,93 +380,6 @@ pre-existing LAN.
|
||||
# forward_ip = 10.55.0.16
|
||||
# forward_port = 4242
|
||||
|
||||
*Please Note!* If you use the ``device`` option, you will need the Python module
|
||||
``netifaces`` installed on your system. You can install it with ``pip3 install netifaces``.
|
||||
|
||||
.. _interfaces-tcps:
|
||||
|
||||
TCP Server Interface
|
||||
====================
|
||||
|
||||
The TCP Server interface is suitable for allowing other peers to connect over
|
||||
the Internet or private IP networks. When a TCP server interface has been
|
||||
configured, other Reticulum peers can connect to it with a TCP Client interface.
|
||||
|
||||
.. code::
|
||||
|
||||
# This example demonstrates a TCP server interface.
|
||||
# It will listen for incoming connections on the
|
||||
# specified IP address and port number.
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
|
||||
# Alternatively you can bind to a specific IP
|
||||
|
||||
# listen_ip = 10.0.0.88
|
||||
# listen_port = 4242
|
||||
|
||||
# Or a specific network device
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
*Please Note!* If you use the ``device`` option, you will need the Python module
|
||||
``netifaces`` installed on your system. You can install it with ``pip3 install netifaces``.
|
||||
|
||||
.. _interfaces-tcpc:
|
||||
|
||||
TCP Client Interface
|
||||
====================
|
||||
|
||||
To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface. The
|
||||
# target_host can either be an IP address or a hostname.
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
It is also possible to use this interface type to connect via other programs
|
||||
or hardware devices that expose a KISS interface on a TCP port, for example
|
||||
software-based soundmodems. To do this, use the ``kiss_framing`` option:
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface that connects
|
||||
# to a software TNC soundmodem on a KISS over TCP port.
|
||||
|
||||
[[TCP KISS Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
kiss_framing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 8001
|
||||
|
||||
**Caution!** Only use the KISS framing option when connecting to external devices
|
||||
and programs like soundmodems and similar over TCP. When using the
|
||||
``TCPClientInterface`` in conjunction with the ``TCPServerInterface`` you should
|
||||
never enable ``kiss_framing``, since this will disable internal reliability and
|
||||
recovery mechanisms that greatly improves performance over unreliable and
|
||||
intermittent TCP links.
|
||||
|
||||
|
||||
.. _interfaces-rnode:
|
||||
|
||||
@@ -256,11 +400,6 @@ can be used, and offers full control over LoRa parameters.
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = True
|
||||
|
||||
# Allow transmit on interface. Setting
|
||||
# this to false will create a listen-
|
||||
# only interface.
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
|
||||
@@ -311,7 +450,6 @@ directly over a wire-pair, or for using devices such as data radios and lasers.
|
||||
[[Serial Interface]]
|
||||
type = SerialInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
@@ -338,7 +476,6 @@ for station identification purposes.
|
||||
[[Packet Radio KISS Interface]]
|
||||
type = KISSInterface
|
||||
interface_enabled = True
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB1
|
||||
@@ -409,9 +546,6 @@ beaconing functionality described above.
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = True
|
||||
|
||||
# Allow transmit on interface.
|
||||
outgoing = True
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB2
|
||||
|
||||
@@ -443,4 +577,4 @@ beaconing functionality described above.
|
||||
# Whether to use KISS flow-control.
|
||||
# This is useful for modems with a
|
||||
# small internal packet buffer.
|
||||
flow_control = false
|
||||
flow_control = false
|
||||
|
||||
@@ -27,6 +27,11 @@ with Reticulum:
|
||||
While the adress space can support billions of endpoints, Reticulum is
|
||||
also very useful when just a few devices needs to communicate.
|
||||
|
||||
* | Low-bandwidth networks, like LoRa and packet radio, can interoperate and
|
||||
interconnect with much larger and higher bandwidth networks without issue.
|
||||
Reticulum automatically manages the flow of information to and from various
|
||||
network segments, and when bandwidth is limited, local traffic is prioritised.
|
||||
|
||||
* | Reticulum provides sender/initiator anonymity by default. There is no way
|
||||
to filter traffic or discriminate it based on the source of the traffic.
|
||||
|
||||
@@ -47,18 +52,32 @@ with Reticulum:
|
||||
transport node. Letting every node be a transport node will in most cases
|
||||
degrade the performance and reliability of the network.
|
||||
|
||||
In general terms, if a node is stationary, well-connected and kept running
|
||||
*In general terms, if a node is stationary, well-connected and kept running
|
||||
most of the time, it is a good candidate to be a transport node. For optimal
|
||||
performance, a network should contain the amount of transport nodes that
|
||||
provides connectivity to the intended area / topography, and not many more
|
||||
than that.
|
||||
than that.*
|
||||
|
||||
* | Reticulum is designed to work reliably in open, trustless environments. This
|
||||
means you can use it to create open-access networks, where participants can
|
||||
join and leave in an free and unorganised manner. This property allows an
|
||||
entirely new, and so far, mostly unexplored class of networked applications,
|
||||
where networks, and the information flow within them can form and dissolve
|
||||
organically.
|
||||
|
||||
* | You can just as easily create closed networks, since Reticulum allows you to
|
||||
add authentication to any interface. This means you can restrict access on
|
||||
any interface type, even when using legacy devices, such as modems. You can
|
||||
also mix authenticated and open interfaces on the same system. See the
|
||||
:ref:`Common Interface Options<interfaces-options>` section of the :ref:`Interfaces<interfaces-main>`
|
||||
chapter of this manual for information on how to set up interface authentication.
|
||||
|
||||
|
||||
Reticulum allows you to mix very different kinds of networking mediums into a
|
||||
unified mesh, or to keep everything within one medium. You could build a "virtual
|
||||
network" running entirely over the Internet, where all nodes communicate over TCP
|
||||
and UDP "channels". You could also build such a network using MQTT or ZeroMQ as
|
||||
the underlying carrier for Reticulum.
|
||||
and UDP "channels". You could also build such a network using other already-established
|
||||
communications channels as the underlying carrier for Reticulum.
|
||||
|
||||
However, most real-world networks will probably involve either some form of
|
||||
wireless or direct hardline communications. To allow Reticulum to communicate
|
||||
|
||||
@@ -3,20 +3,21 @@
|
||||
***********************
|
||||
Understanding Reticulum
|
||||
***********************
|
||||
This chapter will briefly describe the overall purpose and operating principles of Reticulum, a
|
||||
networking stack designed for reliable and secure communication over high-latency, low-bandwidth
|
||||
links. It should give you an overview of how the stack works, and an understanding of how to
|
||||
This chapter will briefly describe the overall purpose and operating principles of Reticulum.
|
||||
It should give you an overview of how the stack works, and an understanding of how to
|
||||
develop networked applications using Reticulum.
|
||||
|
||||
This document is not an exhaustive source of information on Reticulum, at least not yet. Currently,
|
||||
the best place to go for such information is the Python reference implementation of Reticulum, along
|
||||
with the code examples and API reference. It is however an essential resource to understanding the
|
||||
general principles of Reticulum, how to apply them when creating your own networks or software.
|
||||
This chapter is not an exhaustive source of information on Reticulum, at least not yet. Currently,
|
||||
the only complete repository, and final authority on how Reticulum actually functions, is the Python
|
||||
reference implementation and API reference. That being said, this chapter is an essential resource in
|
||||
understanding how Reticulum works from a high-level perspective, along with the general principles of
|
||||
Reticulum, and how to apply them when creating your own networks or software.
|
||||
|
||||
After reading this document, you should be well-equipped to understand how a Reticulum network
|
||||
operates, what it can achieve, and how you can use it yourself. If you want to help out with the
|
||||
development, this is also the place to start, since it will provide a pretty clear overview of the
|
||||
sentiments and the philosophy behind Reticulum.
|
||||
sentiments and the philosophy behind Reticulum, what problems it seeks to solve, and how it
|
||||
approaches those solutions.
|
||||
|
||||
.. _understanding-motivation:
|
||||
|
||||
@@ -25,34 +26,42 @@ Motivation
|
||||
|
||||
The primary motivation for designing and implementing Reticulum has been the current lack of
|
||||
reliable, functional and secure minimal-infrastructure modes of digital communication. It is my
|
||||
belief that it is highly desirable to create a cheap and reliable way to set up a wide-range digital
|
||||
communication network that can securely allow exchange of information between people and
|
||||
belief that it is highly desirable to create a reliable and efficient way to set up long-range digital
|
||||
communication networks that can securely allow exchange of information between people and
|
||||
machines, with no central point of authority, control, censorship or barrier to entry.
|
||||
|
||||
Almost all of the various networking systems in use today share a common limitation, namely that they
|
||||
require large amounts of coordination and trust to work, and to join the networks you need approval
|
||||
Almost all of the various networking systems in use today share a common limitation: They
|
||||
require large amounts of coordination and centralised trust and power to function. To join such networks, you need approval
|
||||
of gatekeepers in control. This need for coordination and trust inevitably leads to an environment of
|
||||
central control, where it's very easy for infrastructure operators or governments to control or alter
|
||||
traffic, and censor or persecute unwanted actors.
|
||||
traffic, and censor or persecute unwanted actors. It also makes it completely impossible to freely deploy
|
||||
and use networks at will, like one would use other common tools that enhance individual agency and freedom.
|
||||
|
||||
Reticulum aims to require as little coordination and trust as possible. In fact, the only
|
||||
“coordination” required is to know the characteristics of physical medium carrying Reticulum traffic.
|
||||
Reticulum aims to require as little coordination and trust as possible. It aims to make secure,
|
||||
anonymous and permissionless networking and information exchange a tool that anyone can just pick up and use.
|
||||
|
||||
Since Reticulum is completely medium agnostic, this could be whatever is best suited to the situation.
|
||||
In some cases, this might be 1200 baud packet radio links over VHF frequencies, in other cases it might
|
||||
be a microwave network using off-the-shelf radios. At the time of release of this document, the
|
||||
recommended setup for development and testing is using LoRa radio modules with an open source firmware
|
||||
(see the section :ref:`Reference System Setup<understanding-referencesystem>`), connected to a small
|
||||
computer like a Raspberry Pi. As an example, the default reference setup provides a channel capacity
|
||||
of 5.4 Kbps, and a usable direct node-to-node range of around 15 kilometers (indefinitely extendable
|
||||
by using multiple hops).
|
||||
Since Reticulum is completely medium agnostic, it can be used to build networks on whatever is best
|
||||
suited to the situation, or whatever you have available. In some cases, this might be packet radio
|
||||
links over VHF frequencies, in other cases it might be a 2.4 GHz
|
||||
network using off-the-shelf radios, or it might be using common LoRa development boards.
|
||||
|
||||
At the time of release of this document, the fastest and easiest setup for development and testing is using
|
||||
LoRa radio modules with an open source firmware (see the section :ref:`Reference Setup<understanding-referencesystem>`),
|
||||
connected to any kind of computer or mobile device that Reticulum can run on.
|
||||
|
||||
The ultimate aim of Reticulum is to allow anyone to be their own network operator, and to make it
|
||||
cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks.
|
||||
Reticulum **is not** *one network*, it **is a tool** to build *thousands of networks*.
|
||||
|
||||
Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
|
||||
.. _understanding-goals:
|
||||
|
||||
Goals
|
||||
=====
|
||||
|
||||
To be as widely usable and easy to use as possible, the following goals have been used to
|
||||
To be as widely usable and efficient to deploy as possible, the following goals have been used to
|
||||
guide the design of Reticulum:
|
||||
|
||||
|
||||
@@ -60,14 +69,14 @@ guide the design of Reticulum:
|
||||
Reticulum must be implemented with, and be able to run using only open source software. This is
|
||||
critical to ensuring the availability, security and transparency of the system.
|
||||
* **Hardware layer agnosticism**
|
||||
Reticulum shall be fully hardware agnostic, and shall be useable over a wide range
|
||||
Reticulum must be fully hardware agnostic, and shall be useable over a wide range of
|
||||
physical networking layers, such as data radios, serial lines, modems, handheld transceivers,
|
||||
wired ethernet, wifi, or anything else that can carry a digital data stream. Hardware made for
|
||||
dedicated Reticulum use shall be as cheap as possible and use off-the-shelf components, so
|
||||
it can be easily replicated.
|
||||
it can be easily modified and replicated by anyone interested in doing so.
|
||||
* **Very low bandwidth requirements**
|
||||
Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||
as *500 bps*.
|
||||
as *500 bits per second*.
|
||||
* **Encryption by default**
|
||||
Reticulum must use strong encryption by default for all communication.
|
||||
* **Initiator Anonymity**
|
||||
@@ -79,15 +88,15 @@ guide the design of Reticulum:
|
||||
frequency bands, and can provide functional long distance links in such conditions, for example
|
||||
by connecting a modem to a PMR or CB radio, or by using LoRa or WiFi modules.
|
||||
* **Supplied software**
|
||||
Apart from the core networking stack and API, that allows a developer to build
|
||||
applications with Reticulum, a basic communication suite using Reticulum must be
|
||||
implemented and released at the same time as Reticulum itself. This shall serve both as a
|
||||
functional communication suite, and as an example and learning resource to others wishing
|
||||
In addition to the core networking stack and API, that allows a developer to build
|
||||
applications with Reticulum, a basic set of Reticulum-based communication tools must be
|
||||
implemented and released along with Reticulum itself. These shall serve both as a
|
||||
functional, basic communication suite, and as an example and learning resource to others wishing
|
||||
to build applications with Reticulum.
|
||||
* **Ease of use**
|
||||
The reference implementation of Reticulum is written in Python, to make it easy to use
|
||||
and understand. A programmer with only basic experience should be able to use
|
||||
Reticulum in their own applications.
|
||||
Reticulum to write networked applications.
|
||||
* **Low cost**
|
||||
It shall be as cheap as possible to deploy a communication system based on Reticulum. This
|
||||
should be achieved by using cheap off-the-shelf hardware that potential users might already
|
||||
@@ -109,20 +118,29 @@ Reticulum uses the singular concept of *destinations*. Any application using Ret
|
||||
networking stack will need to create one or more destinations to receive data, and know the
|
||||
destinations it needs to send data to.
|
||||
|
||||
All destinations in Reticulum are represented internally as 10 bytes, derived from truncating a full
|
||||
All destinations in Reticulum are represented as a 10 byte hash, derived from truncating a full
|
||||
SHA-256 hash of identifying characteristics of the destination. To users, the destination addresses
|
||||
will be displayed as 10 bytes in hexadecimal representation, as in the following example: ``<80e29bf7cccaf31431b3>``.
|
||||
|
||||
The truncation size of 10 bytes (80 bits) for destinations has been choosen as a reasonable tradeoff between address space
|
||||
and packet overhead. The address space accomodated by this size can support many billions of
|
||||
simultaneously active devices on the same network, while keeping packet overhead low, which is
|
||||
essential on low-bandwidth networks. In the very unlikely case that this address space nears
|
||||
congestion, a one-line code change can upgrade the Reticulum address space all the way up to 256
|
||||
bits, ensuring the Reticulum address space could potentially support galactic-scale networks.
|
||||
This is obviusly complete and ridiculous over-allocation, and as such, the current 80 bits should
|
||||
be sufficient, even far into the future.
|
||||
|
||||
By default Reticulum encrypts all data using elliptic curve cryptography. Any packet sent to a
|
||||
destination is encrypted with a derived ephemeral key. Reticulum can also set up an encrypted
|
||||
channel to a destination with *Forward Secrecy* and *Initiator Anonymity* using a elliptic
|
||||
curve cryptography and ephemeral keys derived from a Diffie Hellman exchange on Curve25519. In
|
||||
Reticulum terminology, this is called a *Link*.
|
||||
Reticulum terminology, this is called a *Link*. The multi-hop transport, coordination, verification
|
||||
and reliability layers are fully autonomous and also based on elliptic curve cryptography.
|
||||
|
||||
Reticulum also offers symmetric key encryption for group-oriented communications, as well as
|
||||
unencrypted packets for broadcast purposes, or situations where you need the communication to be in
|
||||
plain text. The multi-hop transport, coordination, verification and reliability layers are fully
|
||||
autonomous and based on public key cryptography.
|
||||
plain text.
|
||||
|
||||
Reticulum can connect to a variety of interfaces such as radio modems, data radios and serial ports,
|
||||
and offers the possibility to easily tunnel Reticulum traffic over IP links such as the Internet or
|
||||
@@ -138,22 +156,30 @@ destinations. Reticulum uses three different basic destination types, and one sp
|
||||
|
||||
|
||||
* **Single**
|
||||
The *single* destination type is always identified by a unique public key. Any data sent to this
|
||||
The *single* destination type is the most common type in Reticulum, and should be used for
|
||||
most purposes. It is always identified by a unique public key. Any data sent to this
|
||||
destination will be encrypted using ephemeral keys derived from an ECDH key exchange, and will
|
||||
only be readable by the creator of the destination, who holds the corresponding private key.
|
||||
* **Group**
|
||||
The *group* destination type defines a symmetrically encrypted destination. Data sent to this
|
||||
destination will be encrypted with a symmetric key, and will be readable by anyone in
|
||||
possession of the key.
|
||||
* **Plain**
|
||||
A *plain* destination type is unencrypted, and suited for traffic that should be broadcast to a
|
||||
number of users, or should be readable by anyone. Traffic to a *plain* destination is not encrypted.
|
||||
Generally, *plain* destinations can be used for broadcast information intended to be public.
|
||||
Plain destinations are only reachable directly, and packets adressed to plain destinations are
|
||||
never transported over multiple hops in the network. To be transportable over multiple hops in Reticulum, information
|
||||
*must* be encrypted, since Reticulum uses the per-packet encryption to verify routing paths and
|
||||
keep them alive.
|
||||
* **Group**
|
||||
The *group* special destination type, that defines a symmetrically encrypted virtual destination.
|
||||
Data sent to this destination will be encrypted with a symmetric key, and will be readable by
|
||||
anyone in possession of the key, but as with the *plain* destination type, packets to this type
|
||||
of destination are not currently transported over multiple hops, although a planned upgrade
|
||||
to Reticulum will allow globally reachable *group* destinations.
|
||||
* **Link**
|
||||
A *link* is a special destination type, that serves as an abstract channel to a *single*
|
||||
destination, directly connected or over multiple hops. The *link* also offers reliability and
|
||||
more efficient encryption, forward secrecy, initiator anonymity, and as such can be useful even
|
||||
when a node is directly reachable.
|
||||
when a node is directly reachable. It also offers a more capable API and allows easily carrying
|
||||
out requests and responses, large data transfers and more.
|
||||
|
||||
.. _understanding-destinationnaming:
|
||||
|
||||
@@ -195,7 +221,7 @@ packet.
|
||||
In actual use of *single* destination naming, it is advisable not to use any uniquely identifying
|
||||
features in aspect naming. Aspect names should be general terms describing what kind of destination
|
||||
is represented. The uniquely identifying aspect is always acheived by the appending the public key,
|
||||
which expands the destination into a uniquely identifyable one.
|
||||
which expands the destination into a uniquely identifyable one. Reticulum does this automatically.
|
||||
|
||||
Any destination on a Reticulum network can be addressed and reached simply by knowning its
|
||||
destination hash (and public key, but if the public key is not known, it can be requested from the
|
||||
@@ -211,30 +237,32 @@ To recap, the different destination types should be used in the following situat
|
||||
When private communication between two or more endpoints is needed. Supports multiple hops
|
||||
indirectly, but must first be established through a *single* destination.
|
||||
* **Plain**
|
||||
When plain-text communication is desirable, for example when broadcasting information.
|
||||
When plain-text communication is desirable, for example when broadcasting information, or for local discovery purposes.
|
||||
|
||||
To communicate with a *single* destination, you need to know it’s public key. Any method for
|
||||
obtaining the public key is valid, but Reticulum includes a simple mechanism for making other
|
||||
nodes aware of your destinations public key, called the *announce*. It is also possible to request
|
||||
an unknown public key from the network, as all participating nodes serve as a distributed ledger
|
||||
an unknown public key from the network, as all transport instances serve as a distributed ledger
|
||||
of public keys.
|
||||
|
||||
Note that public key information can be shared and verified in many other ways than using the
|
||||
built-in *announce* functionality, and that it is therefore not required to use the announce/request
|
||||
Note that public key information can be shared and verified in other ways than using the
|
||||
built-in *announce* functionality, and that it is therefore not required to use the *announce* and *path request*
|
||||
functionality to obtain public keys. It is by far the easiest though, and should definitely be used
|
||||
if there is not a good reason for doing it differently.
|
||||
if there is not a very good reason for doing it differently.
|
||||
|
||||
.. _understanding-keyannouncements:
|
||||
|
||||
Public Key Announcements
|
||||
------------------------
|
||||
|
||||
An *announce* will send a special packet over any configured interfaces, containing all needed
|
||||
An *announce* will send a special packet over any relevant interfaces, containing all needed
|
||||
information about the destination hash and public key, and can also contain some additional,
|
||||
application specific data. The entire packet is signed by the sender to ensure authenticity. It is not
|
||||
required to use the announce functionality, but in many cases it will be the simplest way to share
|
||||
public keys on the network. As an example, an announce in a simple messenger application might
|
||||
contain the following information:
|
||||
public keys on the network. The announce mechanism also serves to establish end-to-end connectivity
|
||||
to the announced destination, as the announce propagates through the network.
|
||||
|
||||
As an example, an announce in a simple messenger application might contain the following information:
|
||||
|
||||
|
||||
* The announcers destination hash
|
||||
@@ -247,13 +275,21 @@ With this information, any Reticulum node that receives it will be able to recon
|
||||
destination to securely communicate with that destination. You might have noticed that there is one
|
||||
piece of information lacking to reconstruct full knowledge of the announced destination, and that is
|
||||
the aspect names of the destination. These are intentionally left out to save bandwidth, since they
|
||||
will be implicit in almost all cases. If a destination name is not entirely implicit, information can be
|
||||
included in the application specific data part that will allow the receiver to infer the naming.
|
||||
will be implicit in almost all cases. The receiving application will already know them. If a destination
|
||||
name is not entirely implicit, information can be included in the application specific data part that
|
||||
will allow the receiver to infer the naming.
|
||||
|
||||
It is important to note that announces will be forwarded throughout the network according to a
|
||||
certain pattern. This will be detailed in the section
|
||||
:ref:`The Announce Mechanism in Detail<understanding-announce>`.
|
||||
|
||||
In Reticulum, destinations are allowed to move around the network at will. This is very different from
|
||||
protocols such as IP, where an address is always expected to stay within the network segment it was assigned in.
|
||||
This limitation does not exist in Reticulum, and any destination is *completely portable* over the entire topography
|
||||
of the network, and *can even be moved to other Reticulum networks* than the one it was created in, and
|
||||
still become reachable. To update it's reachability, a destination simply needs to send an announce on any
|
||||
networks it is part of. After a short while, it will be globally reachable in the network.
|
||||
|
||||
Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic.
|
||||
|
||||
.. _understanding-identities:
|
||||
@@ -262,21 +298,22 @@ Identities
|
||||
----------
|
||||
|
||||
In Reticulum, an *identity* does not necessarily represent a personal identity, but is an abstraction that
|
||||
can represent any kind of *verified entity*. This could very well be a person, but it could also be the
|
||||
can represent any kind of *verifiable entity*. This could very well be a person, but it could also be the
|
||||
control interface of a machine, a program, robot, computer, sensor or something else entirely. In
|
||||
general, any kind of agent that can act, or be acted upon, or store or manipulate information, can be
|
||||
represented as an identity.
|
||||
represented as an identity. An *identity* can be used to create any number of destinations.
|
||||
|
||||
As we have seen, a *single* destination will always have an *identity* tied to it, but not *plain* or *group*
|
||||
A *single* destination will always have an *identity* tied to it, but not *plain* or *group*
|
||||
destinations. Destinations and identities share a multilateral connection. You can create a
|
||||
destination, and if it is not connected to an identity upon creation, it will just create a new one to use
|
||||
automatically. This may be desirable in some situations, but often you will probably want to create
|
||||
the identity first, and then link it to created destinations.
|
||||
the identity first, and then use it to create new destinations.
|
||||
|
||||
Building upon the simple messenger example, we could use an identity to represent the user of the
|
||||
application. Destinations created will then be linked to this identity to allow communication to
|
||||
reach the user. In all cases it is of great importance to store the private keys associated with any
|
||||
Reticulum Identity securely and privately.
|
||||
As an example, we could use an identity to represent the user of a messaging application.
|
||||
Destinations can then be created by this identity to allow communication to reach the user.
|
||||
In all cases it is of great importance to store the private keys associated with any
|
||||
Reticulum Identity securely and privately, since obtaining access to the identity keys equals
|
||||
obtaining access and controlling reachability to any destinations created by that identity.
|
||||
|
||||
.. _understanding-gettingfurther:
|
||||
|
||||
@@ -295,57 +332,73 @@ In the following sections, two concepts that allow this will be introduced, *pat
|
||||
Reticulum Transport
|
||||
===================
|
||||
|
||||
The term routing has been purposefully avoided until now. The current methods of routing used in IP-based
|
||||
networks are fundamentally incompatible with the physical link types that Reticulum was designed to handle.
|
||||
These routing methodologies assume trust at the physical layer, and often needs a lot more bandwidth than
|
||||
Reticulum can assume is available.
|
||||
The methods of routing used in traditional networks are fundamentally incompatible with the physical medium
|
||||
types and circumstances that Reticulum was designed to handle. These mechanisms mostly assume trust at the physical layer,
|
||||
and often needs a lot more bandwidth than Reticulum can assume is available. Since Reticulum is designed to
|
||||
survive running over open radio spectrum, no such trust can be assumed, and bandwidth is often very limited.
|
||||
|
||||
Since Reticulum is designed to run over open radio spectrum, no such trust exists, and bandwidth is often
|
||||
very limited. Existing routing protocols like BGP or OSPF carry too much overhead to be practically
|
||||
useable over bandwidth-limited, high-latency links.
|
||||
|
||||
To overcome such challenges, Reticulum’s *Transport* system uses public-key cryptography to
|
||||
implement the concept of *paths* that allow discovery of how to get information to a certain
|
||||
To overcome such challenges, Reticulum’s *Transport* system uses asymmetric elliptic curve cryptography to
|
||||
implement the concept of *paths* that allow discovery of how to get information closer to a certain
|
||||
destination. It is important to note that no single node in a Reticulum network knows the complete
|
||||
path to a destination. Every Transport node participating in a Reticulum network will only
|
||||
know what the most direct way to get a packet one hop closer to it's destination is.
|
||||
know the most direct way to get a packet one hop closer to it's destination.
|
||||
|
||||
|
||||
.. _understanding-nodetypes:
|
||||
|
||||
Node Types
|
||||
----------
|
||||
|
||||
Currently, Reticulum distinguishes between two types of network nodes. All nodes on a Reticulum network
|
||||
are *Reticulum Instances*, and some are alo *Transport Nodes*. If a system running Reticulum is fixed in
|
||||
one place, and is intended to be kept available most of the time, it is a good contender to be a *Transport Node*.
|
||||
|
||||
Any Reticulum Instance can become a Transport Node by enabling it in the configuration.
|
||||
This distinction is made by the user configuring the node, and is used to determine what nodes on the
|
||||
network will help forward traffic, and what nodes rely on other nodes for wider connectivity.
|
||||
|
||||
If a node is an *Instance* it should be given the configuration directive ``enable_transport = No``, which
|
||||
is the default setting.
|
||||
|
||||
If it is a *Transport Node*, it should be given the configuration directive ``enable_transport = Yes``.
|
||||
|
||||
|
||||
.. _understanding-announce:
|
||||
|
||||
The Announce Mechanism in Detail
|
||||
--------------------------------
|
||||
|
||||
When an *announce* is transmitted by a node, it will be forwarded by any node receiving it, but
|
||||
according to some specific rules:
|
||||
When an *announce* for a destination is transmitted by from a Reticulum instance, it will be forwarded by
|
||||
any transport node receiving it, but according to some specific rules:
|
||||
|
||||
|
||||
* | If this exact announce has already been received before, ignore it.
|
||||
|
||||
* | If not, record into a table which node the announce was received from, and how many times in
|
||||
* | If not, record into a table which Transport Node the announce was received from, and how many times in
|
||||
total it has been retransmitted to get here.
|
||||
|
||||
* | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is
|
||||
set to 18.
|
||||
* | If the announce has been retransmitted *m+1* times, it will not be forwarded any more. By default, *m* is
|
||||
set to 128.
|
||||
|
||||
* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, and *h* is the amount of times this packet has already been forwarded.
|
||||
* | After a randomised delay, the announce will be retransmitted on all interfaces that have bandwidth
|
||||
available for processing announces. By default, the maximum bandwidth allocation for processing
|
||||
announces is set at 2%, but can be configured on a per-interface basis.
|
||||
|
||||
* | The packet will be given a priority *p = 1/d*.
|
||||
* | If any given interface does not have enough bandwidth available for retransmitting the announce,
|
||||
the announce will be assigned a priority inversely proportional to it's hop count, and be inserted
|
||||
into a queue managed by the interface.
|
||||
|
||||
* | If at least *d* seconds has passed since the announce was received, and no other packets with a
|
||||
priority higher than *p* are waiting in the queue (see Packet Prioritisation), and the channel is
|
||||
not utilized by other traffic, the announce will be forwarded.
|
||||
* | When the interface has bandwidth available for processing an announce, it will prioritise announces
|
||||
for destinations that are closest in terms of hops, thus prioritising reachability and connectivity
|
||||
of local nodes, even on slow networks that connect to wider and faster networks.
|
||||
|
||||
* | If no other nodes are heard retransmitting the announce with a greater hop count than when
|
||||
it left this node, transmitting it will be retried *r* times. By default, *r* is set to 1. Retries
|
||||
follow same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` +
|
||||
t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
|
||||
node to retransmit the packet, plus a random window. By default, *t* is set to 10 seconds, and the
|
||||
random window *rw* is set to 10 seconds.
|
||||
* | After the announce has been re-transmitted, and if no other nodes are heard retransmitting the announce
|
||||
with a greater hop count than when it left this node, transmitting it will be retried *r* times. By default,
|
||||
*r* is set to 1.
|
||||
|
||||
* | If a newer announce from the same destination arrives, while an identical one is already in
|
||||
the queue, the newest announce is discarded. If the newest announce contains different
|
||||
application specific data, it will replace the old announce, but will use *d* and *p* of the old
|
||||
announce.
|
||||
* | If a newer announce from the same destination arrives, while an identical one is already waiting
|
||||
to be transmitted, the newest announce is discarded. If the newest announce contains different
|
||||
application specific data, it will replace the old announce.
|
||||
|
||||
Once an announce has reached a node in the network, any other node in direct contact with that
|
||||
node will be able to reach the destination the announce originated from, simply by sending a packet
|
||||
@@ -353,11 +406,16 @@ addressed to that destination. Any node with knowledge of the announce will be a
|
||||
packet towards the destination by looking up the next node with the shortest amount of hops to the
|
||||
destination.
|
||||
|
||||
According to these rules and default constants, an announce will propagate throughout the network
|
||||
in a predictable way. In an example network utilising the default constants, and with an average link
|
||||
distance of *Lavg =* 15 kilometers, an announce will be able to propagate outwards to a radius of 180
|
||||
kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3
|
||||
days.
|
||||
According to these rules, an announce will propagate throughout the network in a predictable way,
|
||||
and make the announced destination reachable in a short amount of time. Fast networks that have the
|
||||
capacity to process many announces can reach full convergence very quickly, even when constantly adding
|
||||
new destinations. Slower segments of such networks might take a bit longer to gain full knowledge about
|
||||
the wide and fast networks they are connected to, but can still do so over time, while prioritising full
|
||||
and quickly converging end-to-end connectivity for their local, slower segments.
|
||||
|
||||
In general, even extremely complex networks, that utilize the maximum 128 hops will converge to full
|
||||
end-to-end connectivity in about one minute, given there is enough bandwidth available to process
|
||||
the required amount of announces.
|
||||
|
||||
.. _understanding-paths:
|
||||
|
||||
@@ -404,7 +462,7 @@ For exchanges of small amounts of information, Reticulum offers the *Packet* API
|
||||
For exchanges of larger amounts of data, or when longer sessions of bidirectional communication is desired, Reticulum offers the *Link* API. To establish a *link*, the following process is employed:
|
||||
|
||||
* | First, the node that wishes to establish a link will send out a special packet, that
|
||||
traverses the network and locates the desired destination. Along the way, the nodes that
|
||||
traverses the network and locates the desired destination. Along the way, the Transport Nodes that
|
||||
forward the packet will take note of this *link request*.
|
||||
|
||||
* | Second, if the destination accepts the *link request* , it will send back a packet that proves the
|
||||
@@ -415,15 +473,19 @@ For exchanges of larger amounts of data, or when longer sessions of bidirectiona
|
||||
* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will
|
||||
remember the *link* , and it can subsequently be used by referring to a hash representing it.
|
||||
|
||||
* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an
|
||||
efficiently encrypted tunnel between the two nodes, using elliptic curve cryptography. As such,
|
||||
this mode of communication is preferred, even for situations when nodes can directly communicate,
|
||||
when the amount of data to be exchanged numbers in the tens of packets.
|
||||
* | As a part of the *link request*, an Elliptic Curve Diffie-Hellman key exchange takes place, that sets up an
|
||||
efficiently encrypted tunnel between the two nodes. As such, this mode of communication is preferred,
|
||||
even for situations when nodes can directly communicate, when the amount of data to be exchanged numbers
|
||||
in the tens of packets, or whenever the use of the more advanced API functions is desired.
|
||||
|
||||
* | When a *link* has been set up, it automatically provides message receipt functionality, through
|
||||
the same *proof* mechanism discussed before, so the sending node can obtain verified confirmation
|
||||
that the information reached the intended recipient.
|
||||
|
||||
* | Once the *link* has been set up, the initiator can remain anonymous, or choose to authenticate towards
|
||||
the destination using a Reticulum Identity. This authentication is happening inside the encrypted
|
||||
link, and is only revealed to the verified destination, and no intermediaries.
|
||||
|
||||
In a moment, we will discuss the details of how this methodology is implemented, but let’s first
|
||||
recap what purposes this methodology serves. We first ensure that the node answering our request
|
||||
is actually the one we want to communicate with, and not a malicious actor pretending to be so.
|
||||
@@ -518,57 +580,62 @@ or stream data directly from files.
|
||||
|
||||
.. _understanding-referencesystem:
|
||||
|
||||
Reference System Setup
|
||||
Reference Setup
|
||||
======================
|
||||
|
||||
This section will detail the recommended *Reference System Setup* for Reticulum. It is important to
|
||||
note that Reticulum is designed to be usable over more or less any medium that allows you to send
|
||||
and receive data in a digital form, and satisfies some very low minimum requirements. The
|
||||
communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 1000 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
Reticulum software should be able to run on more or less any hardware that can provide a Python 3.x
|
||||
This section will detail a recommended *Reference Setup* for Reticulum. It is important to
|
||||
note that Reticulum is designed to be usable on more or less any computing device, and over more
|
||||
or less any medium that allows you to send and receive data, which satisfies some very low
|
||||
minimum requirements.
|
||||
|
||||
The communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
||||
runtime environment.
|
||||
|
||||
That being said, the reference setup has been outlined to provide a common platform for anyone
|
||||
That being said, this reference setup has been outlined to provide a common platform for anyone
|
||||
who wants to help in the development of Reticulum, and for everyone who wants to know a
|
||||
recommended setup to get started. A reference system consists of three parts:
|
||||
recommended setup to get started experimenting. A reference system consists of three parts:
|
||||
|
||||
* **A channel access device**
|
||||
Or *CAD* , in short, provides access to the physical medium whereupon the communication
|
||||
* **An Interface Device**
|
||||
Which provides access to the physical medium whereupon the communication
|
||||
takes place, for example a radio with an integrated modem. A setup with a separate modem
|
||||
connected to a radio would also be termed a “channel access device”.
|
||||
* **A host device**
|
||||
Some sort of computing device that can run the necessary software, communicates with the
|
||||
channel access device, and provides user interaction.
|
||||
* **A software stack**
|
||||
connected to a radio would also be an interface device.
|
||||
* **A Host Device**
|
||||
Some sort of computing device that can run the necessary software, communicate with the
|
||||
interface device, and provide user interaction.
|
||||
* **A Software Stack**
|
||||
The software implementing the Reticulum protocol and applications using it.
|
||||
|
||||
The reference setup can be considered a relatively stable platform to develop on, and also to start
|
||||
building networks on. While details of the implementation might change at the current stage of
|
||||
building networks or applications on. While details of the implementation might change at the current stage of
|
||||
development, it is the goal to maintain hardware compatibility for as long as entirely possible, and
|
||||
the current reference setup has been determined to provide a functional platform for many years
|
||||
into the future. The current Reference System Setup is as follows:
|
||||
|
||||
|
||||
* **Channel Access Device**
|
||||
* **Interface Device**
|
||||
A data radio consisting of a LoRa radio module, and a microcontroller with open source
|
||||
firmware, that can connect to host devices via USB. It operates in either the 430, 868 or 900
|
||||
MHz frequency bands. More details can be found on the `RNode Page <https://unsigned.io/rnode>`_.
|
||||
* **Host device**
|
||||
* **Host Device**
|
||||
Any computer device running Linux and Python. A Raspberry Pi with a Debian based OS is
|
||||
recommended.
|
||||
* **Software stack**
|
||||
The current Reference Implementation Release of Reticulum, running on a Debian based
|
||||
* **Software Stack**
|
||||
The most recently released Python Implementation of Reticulum, running on a Debian based
|
||||
operating system.
|
||||
|
||||
It is very important to note, that the reference channel access device **does not** use the LoRaWAN
|
||||
standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will
|
||||
need a plain LoRa radio module connected to an MCU with the correct firmware. Full details on how to
|
||||
To avoid confusion, it is very important to note, that the reference interface device **does not**
|
||||
use the LoRaWAN standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will
|
||||
need a plain LoRa radio module connected to an controller with the correct firmware. Full details on how to
|
||||
get or make such a device is available on the `RNode Page <https://unsigned.io/rnode>`_.
|
||||
|
||||
With the current reference setup, it should be possible to get on a Reticulum network for around 100$
|
||||
even if you have none of the hardware already, and need to purchase everything.
|
||||
|
||||
This reference setup is of course just a recommendation for getting started easily, and you should
|
||||
tailor it to your own specific needs, or whatever hardware you have available.
|
||||
|
||||
.. _understanding-protocolspecifics:
|
||||
|
||||
Protocol Specifics
|
||||
@@ -579,20 +646,6 @@ Reticulum, but non critical in understanding how the protocol works on a general
|
||||
treated more as a reference than as essential reading.
|
||||
|
||||
|
||||
Node Types
|
||||
----------
|
||||
|
||||
Currently Reticulum defines two node types, the *Station* and the *Peer*. A node is a *station* if it fixed
|
||||
in one place, and if it is intended to be kept online most of the time. Otherwise the node is a *peer*.
|
||||
|
||||
This distinction is made by the user configuring the node, and is used to determine what nodes on the
|
||||
network will help forward traffic, and what nodes rely on other nodes for connectivity.
|
||||
|
||||
If a node is a *Peer* it should be given the configuration directive ``enable_transport = No``.
|
||||
|
||||
If it is a *Station*, it should be given the configuration directive ``enable_transport = Yes``.
|
||||
|
||||
|
||||
Packet Prioritisation
|
||||
---------------------
|
||||
|
||||
@@ -601,10 +654,29 @@ on a first-come, first-serve basis. Announce re-transmission are handled accordi
|
||||
times and priorities described earlier in this chapter.
|
||||
|
||||
|
||||
Interface Access Codes
|
||||
----------------------
|
||||
|
||||
Reticulum can create named virtual networks, and networks that are only accessible by knowing a preshared
|
||||
passphrase. The configuration of this is detailed in the :ref:`Common Interface Options<interfaces-options>`
|
||||
section. To implement these feature, Reticulum uses the concept of Interface Access Codes, that are calculated
|
||||
and verified per packet.
|
||||
|
||||
An interface with a named virtual network or passphrase authentication enabled will derive a shared Ed25519
|
||||
signing identity, and for every outbound packet generate a signature of the entire packet. This signature is
|
||||
then inserted into the packet as an Interface Access Code before transmission. Depending on the speed and
|
||||
capabilities of the interface, the IFAC can be the full 512-bit Ed25519 signature, or a truncated version.
|
||||
Configured IFAC length can be inspected for all interfaces with the ``rnstatus`` utility.
|
||||
|
||||
Upon receipt, the interface will check that the signature matches the expected value, and drop the packet if it
|
||||
does not. This ensures that only packets sent with the correct naming and/or passphrase parameters are allowed to
|
||||
pass onto the network.
|
||||
|
||||
|
||||
.. _understanding-packetformat:
|
||||
|
||||
Binary Packet Format
|
||||
--------------------
|
||||
Wire Format
|
||||
-----------
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
@@ -615,9 +687,14 @@ Binary Packet Format
|
||||
[HEADER 2 bytes] [ADDRESSES 10/20 bytes] [CONTEXT 1 byte] [DATA 0-477 bytes]
|
||||
|
||||
* The HEADER field is 2 bytes long.
|
||||
* Byte 1: [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 2: Number of hops
|
||||
|
||||
* Interface Access Code field if the IFAC flag was set.
|
||||
* The length of the Interface Access Code can vary from
|
||||
1 to 64 bytes according to physical interface
|
||||
capabilities and configuration.
|
||||
|
||||
* The ADDRESSES field contains either 1 or 2 addresses.
|
||||
* Each address is 10 bytes long.
|
||||
* The Header Type flag in the HEADER field determines
|
||||
@@ -630,12 +707,16 @@ Binary Packet Format
|
||||
* The DATA field is between 0 and 477 bytes.
|
||||
* It contains the packets data payload.
|
||||
|
||||
IFAC Flag
|
||||
-----------------
|
||||
open 0 Packet for publically accessible interface
|
||||
authenticated 1 Interface authentication is included in packet
|
||||
|
||||
|
||||
Header Types
|
||||
-----------------
|
||||
type 1 00 Two byte header, one 10 byte address field
|
||||
type 2 01 Two byte header, two 10 byte address fields
|
||||
type 3 10 Reserved
|
||||
type 4 11 Reserved
|
||||
type 1 0 Two byte header, one 10 byte address field
|
||||
type 2 1 Two byte header, two 10 byte address fields
|
||||
|
||||
|
||||
Propagation Types
|
||||
@@ -664,42 +745,60 @@ Binary Packet Format
|
||||
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD
|
||||
HEADER FIELD DESTINATION FIELDS CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ________________|________________ ________|______ __|_
|
||||
| | | | | | | |
|
||||
01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
| | | | |
|
||||
| | | | +-- Hops = 4
|
||||
| | | +------- Packet Type = DATA
|
||||
| | +--------- Destination Type = SINGLE
|
||||
| +----------- Propagation Type = TRANSPORT
|
||||
+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
01010000 00000100 [HASH1, 10 bytes] [HASH2, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 4
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = TRANSPORT
|
||||
|+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
+- Packet Example -+
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD
|
||||
HEADER FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ _______|_______ ________|______ __|_
|
||||
| | | | | | | |
|
||||
00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
| | | | |
|
||||
| | | | +-- Hops = 7
|
||||
| | | +------- Packet Type = DATA
|
||||
| | +--------- Destination Type = SINGLE
|
||||
| +----------- Propagation Type = BROADCAST
|
||||
+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
00000000 00000111 [HASH1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
+- Packet Example -+
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size including all fields.
|
||||
HEADER FIELD IFAC FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ______|______ _______|_______ ________|______ __|_
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = ENABLED
|
||||
|
||||
- Path Request : 33 bytes
|
||||
- Announce : 151 bytes
|
||||
- Link Request : 77 bytes
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size counting all fields including headers,
|
||||
but excluding any interface access codes.
|
||||
|
||||
- Path Request : 33 bytes
|
||||
- Announce : 151 bytes
|
||||
- Link Request : 77 bytes
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
@@ -6,16 +6,18 @@ Using Reticulum on Your System
|
||||
|
||||
Reticulum is not installed as a driver or kernel module, as one might expect
|
||||
of a networking stack. Instead, Reticulum is distributed as a Python module.
|
||||
This means that no special privileges are required to install or use it.
|
||||
This means that no special privileges are required to install or use it. It
|
||||
is also very light-weight, and easy to transfer to and install on new systems.
|
||||
Any program or application that uses Reticulum will automatically load and
|
||||
initialise Reticulum when it starts.
|
||||
|
||||
In many cases, this approach is sufficient. When any program needs to use
|
||||
Reticulum, it is loaded, initialised, interfaces are brought up, and the
|
||||
program can now communicate over Reticulum. If another program starts up
|
||||
and also wants access to the same Reticulum network, the instance is simply
|
||||
shared. This works for any number of programs running concurrently, and is
|
||||
very easy to use, but depending on your use case, there are other options.
|
||||
program can now communicate over any Reticulum networks available. If another
|
||||
program starts up and also wants access to the same Reticulum network, the
|
||||
instance is simply shared. This works for any number of programs running
|
||||
concurrently, and is very easy to use, but depending on your use case, there
|
||||
are other options.
|
||||
|
||||
Included Utility Programs
|
||||
-------------------------
|
||||
@@ -72,33 +74,49 @@ interfaces, similar to the ``ifconfig`` program.
|
||||
|
||||
# Example output
|
||||
Shared Instance[37428]
|
||||
Status: Up
|
||||
Connected applications: 1
|
||||
RX: 1.13 KB
|
||||
TX: 1.07 KB
|
||||
Status : Up
|
||||
Serving : 1 program
|
||||
Rate : 1.00 Gbps
|
||||
Traffic : 83.13 KB↑
|
||||
86.10 KB↓
|
||||
|
||||
UDPInterface[Default UDP Interface/0.0.0.0:4242]
|
||||
Status: Up
|
||||
RX: 1.01 KB
|
||||
TX: 1.01 KB
|
||||
AutoInterface[Local]
|
||||
Status : Up
|
||||
Mode : Full
|
||||
Rate : 10.00 Mbps
|
||||
Peers : 1 reachable
|
||||
Traffic : 63.23 KB↑
|
||||
80.17 KB↓
|
||||
|
||||
TCPInterface[RNS Testnet Frankfurt/frankfurt.rns.unsigned.io:4965]
|
||||
Status: Up
|
||||
RX: 1.37 KB
|
||||
TX: 9.02 KB
|
||||
Status : Up
|
||||
Mode : Full
|
||||
Rate : 10.00 Mbps
|
||||
Traffic : 187.27 KB↑
|
||||
74.17 KB↓
|
||||
|
||||
RNodeInterface[RNode UHF]
|
||||
Status : Up
|
||||
Mode : Access Point
|
||||
Rate : 1.30 kbps
|
||||
Access : 64-bit IFAC by <…e702c42ba8>
|
||||
Traffic : 8.49 KB↑
|
||||
9.23 KB↓
|
||||
|
||||
Reticulum Transport Instance <5245a8efe1788c6a70e1> running
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnsd [-h] [--config CONFIG] [-v] [-q] [--version]
|
||||
usage: rnstatus [-h] [--config CONFIG] [--version] [-a] [-v]
|
||||
|
||||
Reticulum Network Stack Daemon
|
||||
Reticulum Network Stack Status
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
-v, --verbose
|
||||
-q, --quiet
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-v, --verbose
|
||||
|
||||
|
||||
The rnpath Utility
|
||||
@@ -117,7 +135,8 @@ destinations on the Reticulum network.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnpath.py [-h] [--config CONFIG] [--version] [-v] [destination]
|
||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-d] [-w seconds] [-v]
|
||||
[destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
@@ -128,6 +147,9 @@ destinations on the Reticulum network.
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-d, --drop remove the path to a destination
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
|
||||
|
||||
@@ -177,8 +199,8 @@ These changes will be detailed here.
|
||||
Fixed Serial Port Names
|
||||
=======================
|
||||
|
||||
On a Reticulum node with several serial port based interfaces, it can be
|
||||
beneficial to use the fixed name device nodes for the serial ports, instead
|
||||
On a Reticulum instance with several serial port based interfaces, it can be
|
||||
beneficial to use the fixed device names for the serial ports, instead
|
||||
of the dynamically allocated shorthands such as ``/dev/ttyUSB0``. Under most
|
||||
Debian-based distributions, including Ubuntu and Raspberry Pi OS, these nodes
|
||||
can be found under ``/dev/serial/by-id``.
|
||||
@@ -203,7 +225,7 @@ Here is an example of a packet radio TNC configured as such:
|
||||
slottime = 20
|
||||
|
||||
Using this methodology avoids potential naming mix-ups where physical devices
|
||||
might be plugged and unplugged in different orders, or when node name
|
||||
might be plugged and unplugged in different orders, or when device name
|
||||
assignment varies from one boot to another.
|
||||
|
||||
.. _using-systemd:
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
What is Reticulum?
|
||||
******************
|
||||
|
||||
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, that can operate even with very high latency and extremely low bandwidth.
|
||||
Reticulum is a cryptography-based networking stack for building wide-area networks with readily available hardware, that can continue to operate even with extremely low bandwidth and very high latency.
|
||||
|
||||
Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
|
||||
Reticulum allows you to build wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
|
||||
|
||||
Reticulum is a complete networking stack, and does not need IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
|
||||
|
||||
@@ -13,7 +13,7 @@ No kernel modules or drivers are required. Reticulum runs completely in userland
|
||||
|
||||
Current Status
|
||||
==============
|
||||
Reticulum should currently be considered beta software. All core protocol 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.
|
||||
Reticulum should currently be considered beta software. All core protocol 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 stable at the moment, but could change if absolutely warranted.
|
||||
|
||||
|
||||
What does Reticulum Offer?
|
||||
@@ -24,9 +24,9 @@ What does Reticulum Offer?
|
||||
|
||||
* Complete initiator anonymity, communicate without revealing your identity
|
||||
|
||||
* Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
||||
* Asymmetric encryption based on X25519, and Ed25519 signatures as a basis for all communication
|
||||
|
||||
* Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||
* Forward Secrecy by using ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||
|
||||
* Reticulum uses the `Fernet <https://github.com/fernet/spec/blob/master/Spec.md>`_ specification for on-the-wire / over-the-air encryption
|
||||
|
||||
@@ -44,6 +44,12 @@ What does Reticulum Offer?
|
||||
|
||||
* An intuitive and developer-friendly API
|
||||
|
||||
* Efficient link establishment
|
||||
|
||||
* Total bandwidth cost of setting up a link is only 3 packets, totalling 237 bytes
|
||||
|
||||
* Low cost of keeping links open at only 0.62 bits per second
|
||||
|
||||
* Reliable and efficient transfer of arbritrary amounts of data
|
||||
|
||||
* Reticulum can handle a few bytes of data or files of many gigabytes
|
||||
@@ -52,11 +58,9 @@ What does Reticulum Offer?
|
||||
|
||||
* The API is very easy to use, and provides transfer progress
|
||||
|
||||
* Efficient link establishment
|
||||
* Authentication and virtual network segmentation on all supported interface types
|
||||
|
||||
* Total bandwidth cost of setting up a link is only 3 packets, totalling 237 bytes
|
||||
|
||||
* Low cost of keeping links open at only 0.62 bits per second
|
||||
* Flexible scalability allowing extremely low-bandwidth networks to co-exist and interoperate with large, high-bandwidth networks
|
||||
|
||||
|
||||
Where can Reticulum be Used?
|
||||
@@ -68,9 +72,9 @@ ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||
of the types of interfaces Reticulum was designed for.
|
||||
|
||||
An open-source LoRa-based interface called `RNode <https://unsigned.io/rnode>`_
|
||||
has been designed specifically for use with Reticulum. It is possible to build
|
||||
yourself, or it can be purchased as a complete transceiver that just needs a
|
||||
USB connection to the host.
|
||||
has been designed as an example transceiver that is very suitable for
|
||||
Reticulum. It is possible to build it yourself, to transform a common LoRa
|
||||
development board into one, or it can be purchased as a complete transceiver.
|
||||
|
||||
Reticulum can also be encapsulated over existing IP networks, so there's
|
||||
nothing stopping you from using it over wired ethernet or your local WiFi
|
||||
@@ -80,22 +84,40 @@ self-configuring, resilient and encrypted mesh.
|
||||
|
||||
As an example, it's possible to set up a Raspberry Pi connected to both a
|
||||
LoRa radio, a packet radio TNC and a WiFi network. Once the interfaces are
|
||||
configured, Reticulum will take care of the rest, and any device on the WiFi
|
||||
added, Reticulum will take care of the rest, and any device on the WiFi
|
||||
network can communicate with nodes on the LoRa and packet radio sides of the
|
||||
network, and vice versa.
|
||||
|
||||
Interface Types and Devices
|
||||
===========================
|
||||
Reticulum implements a range of generalised interface types that covers most of the communications hardware that Reticulum can run over. If your hardware is not supported, it's relatively simple to implement an interface class. Currently, the following interfaces are supported:
|
||||
Reticulum implements a range of generalised interface types that covers the communications hardware that Reticulum can run over. If your hardware is not supported, it's relatively simple to implement an interface class. Currently, Reticulum can use the following devices and communication mediums:
|
||||
|
||||
* Any ethernet device
|
||||
|
||||
* WiFi devices
|
||||
|
||||
* Wired ethernet devices
|
||||
|
||||
* Fibre-optic transceivers
|
||||
|
||||
* Data radios with ethernet ports
|
||||
|
||||
* LoRa using `RNode <https://unsigned.io/rnode>`_
|
||||
|
||||
* Can be installed on `many popular LoRa boards <https://github.com/markqvist/rnodeconfigutil#supported-devices>`_
|
||||
|
||||
* Can be purchased as a `ready to use transceiver <https://unsigned.io/rnode>`_
|
||||
|
||||
* Packet Radio TNCs, such as `OpenModem <https://unsigned.io/openmodem>`_
|
||||
|
||||
* Any packet radio TNC in KISS mode
|
||||
|
||||
* Ideal for VHF and UHF radio
|
||||
|
||||
* Any device with a serial port
|
||||
|
||||
* The I2P network
|
||||
|
||||
* TCP over IP networks
|
||||
|
||||
* UDP over IP networks
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.3.0 beta',
|
||||
VERSION: '0.3.5 beta',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Code Examples — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Code Examples — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<li class="right" >
|
||||
<a href="reference.html" title="API Reference"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Code Examples</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -2366,7 +2366,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
|
||||
<li class="right" >
|
||||
<a href="reference.html" title="API Reference"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Code Examples</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Index — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Index — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="#" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -60,10 +60,12 @@
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Resource.advertise">advertise() (RNS.Resource method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Destination.announce">announce() (RNS.Destination method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Destination.announce">announce() (RNS.Destination method)</a>
|
||||
<li><a href="reference.html#RNS.Reticulum.ANNOUNCE_CAP">ANNOUNCE_CAP (RNS.Reticulum attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Destination.app_and_aspects_from_name">app_and_aspects_from_name() (RNS.Destination static method)</a>
|
||||
</li>
|
||||
@@ -416,7 +418,7 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="#" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Getting Started Fast — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Getting Started Fast — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -48,9 +48,23 @@ you want to do. This guide will outline sensible starting paths for different
|
||||
scenarios.</p>
|
||||
<div class="section" id="try-using-a-reticulum-based-program">
|
||||
<h2>Try Using a Reticulum-based Program<a class="headerlink" href="#try-using-a-reticulum-based-program" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If you simply want to try using a program built with Reticulum, you can take
|
||||
a look at <a class="reference external" href="https://github.com/markqvist/nomadnet">Nomad Network</a>, which
|
||||
provides a complete encrypted communications suite built with Reticulum.</p>
|
||||
<p>If you simply want to try using a program built with Reticulum, a few different
|
||||
programs exist that allow basic communication and a range of other useful functions
|
||||
over even extremely low-bandwidth Reticulum networks.</p>
|
||||
<p>These programs will let you get a feel for how Reticulum works. They have been designed
|
||||
to run well over networks based on LoRa or packet radio, but can also be used completely
|
||||
over local WiFi, wired ethernet, the Internet, or any combination.</p>
|
||||
<p>As such, it is easy to get started experimenting, without having to set up any radio
|
||||
transceivers or infrastructure just to try it out. Launching the programs on separate
|
||||
devices connected to the same WiFi network is enough to get started, and physical
|
||||
radio interfaces can then be added later.</p>
|
||||
<div class="section" id="nomad-network">
|
||||
<h3>Nomad Network<a class="headerlink" href="#nomad-network" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The terminal-based program <a class="reference external" href="https://github.com/markqvist/nomadnet">Nomad Network</a>
|
||||
provides a complete encrypted communications suite built with Reticulum. It features
|
||||
encrypted messaging (both direct and delayed-delivery for offline users), file sharing,
|
||||
and has a built-in text-browser and page server with support for dynamically rendered pages,
|
||||
user authentication and more.</p>
|
||||
<a class="reference external image-reference" href="_images/nomadnet_3.png"><img alt="_images/nomadnet_3.png" src="_images/nomadnet_3.png" /></a>
|
||||
<p><a class="reference external" href="https://github.com/markqvist/nomadnet">Nomad Network</a> is a user-facing client
|
||||
for the messaging and information-sharing protocol
|
||||
@@ -63,6 +77,20 @@ for the messaging and information-sharing protocol
|
||||
<span class="n">nomadnet</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><strong>Please Note</strong>: If this is the very first time you use pip to install a program
|
||||
on your system, you might need to reboot your system for your program to become
|
||||
available. If you get a “command not found” error or similar when running the
|
||||
program, reboot your system and try again.</p>
|
||||
</div>
|
||||
<div class="section" id="sideband">
|
||||
<h3>Sideband<a class="headerlink" href="#sideband" title="Permalink to this headline">¶</a></h3>
|
||||
<p>If you would rather use a program with a graphical user interface, you can take
|
||||
a look at <a class="reference external" href="https://unsigned.io/sideband">Sideband</a>, which is available for Android,
|
||||
Linux and macOS.</p>
|
||||
<a class="reference external image-reference" href="_images/sideband_1.png"><img alt="_images/sideband_1.png" class="align-center" src="_images/sideband_1.png" style="width: 400px;" /></a>
|
||||
<p>Sideband is currently in the early stages of development, but already provides basic
|
||||
communication features, and interoperates with Nomad Network, or any other LXMF client.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="using-the-included-utilities">
|
||||
<h2>Using the Included Utilities<a class="headerlink" href="#using-the-included-utilities" title="Permalink to this headline">¶</a></h2>
|
||||
@@ -83,15 +111,80 @@ default is located at <code class="docutils literal notranslate"><span class="pr
|
||||
or use the interactive <code class="docutils literal notranslate"><span class="pre">rnsconfig</span></code> utility.</p>
|
||||
<p>When Reticulum is started for the first time, it will create a default
|
||||
configuration file, with one active interface. This default interface uses
|
||||
your existing ethernet network (if there is one), and only allows you to
|
||||
communicate with other Reticulum peers within your local broadcast domain.</p>
|
||||
your existing ethernet and WiFi networks (if any), and only allows you to
|
||||
communicate with other Reticulum peers within your local broadcast domains.</p>
|
||||
<p>To communicate further, you will have to add one or more interfaces. The default
|
||||
configuration includes a number of examples, ranging from using TCP over the
|
||||
internet, to LoRa and Packet Radio interfaces.</p>
|
||||
<p>With Reticulum, you only need to configure what interfaces you want to communicate
|
||||
over. There is no need to configure address spaces, subnets, routing tables,
|
||||
or other things you might be used to from other network types.</p>
|
||||
<p>Once Reticulums knows which interfaces it should use, it will automatically
|
||||
discover topography and configure transport of data to any destinations it
|
||||
knows about.</p>
|
||||
<p>In situations where you already have an established WiFi or ethernet network, and
|
||||
many devices that want to utilise the same external Reticulum network (for example over
|
||||
LoRa), it will often be sufficient to let one system act as a Reticulum gateway, by
|
||||
adding any external interfaces to this systems configuration, and enabling transport. Any
|
||||
other device on your local WiFi will then be able to connect to this wider Reticulum
|
||||
network just using the default interface configuration.</p>
|
||||
<p>Possibly, the examples in the config file are enough to get you started. If
|
||||
you want more information, you can read the <a class="reference internal" href="networks.html#networks-main"><span class="std std-ref">Building Networks</span></a>
|
||||
and <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Interfaces</span></a> chapters of this manual.</p>
|
||||
</div>
|
||||
<div class="section" id="connecting-reticulum-instances-over-the-internet">
|
||||
<h2>Connecting Reticulum Instances Over the Internet<a class="headerlink" href="#connecting-reticulum-instances-over-the-internet" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Reticulum currently offers two interfaces suitable for connecting instances over the Internet: <a class="reference internal" href="interfaces.html#interfaces-tcps"><span class="std std-ref">TCP</span></a>
|
||||
and <a class="reference internal" href="interfaces.html#interfaces-i2p"><span class="std std-ref">I2P</span></a>. Each interface offers a different set of features, and Reticulum
|
||||
users should carefully choose the interface which best suites their needs.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">TCPServerInterface</span></code> allows users to host an instance accessible over TCP/IP. This
|
||||
method is generally faster, lower latency, and more energy efficient than using <code class="docutils literal notranslate"><span class="pre">I2PInterface</span></code>,
|
||||
however it also leaks more data about the server host.</p>
|
||||
<p>TCP connections reveal the IP address of both your instance and the server to anyone who can
|
||||
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
|
||||
inspecting your packets may be able to record packet metadata like time of transmission and packet size.
|
||||
Even though Reticulum encrypts traffic, TCP does not, so an adversary may be able to use
|
||||
packet inspection to learn that a system is running Reticulum, and what other IP adresses connect to it.
|
||||
Hosting a publicly reachable instance over TCP also requires a publicly reachable IP address,
|
||||
which most Internet connections don’t offer anymore.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">I2PInterface</span></code> routes messages through the <a class="reference external" href="https://geti2p.net/en/">Invisible Internet Protocol
|
||||
(I2P)</a>. To properly use this interface, users must also run an I2P daemon in
|
||||
parallel to <code class="docutils literal notranslate"><span class="pre">rnsd</span></code>. For always-on I2P nodes it is recommended to use <a class="reference external" href="https://i2pd.website/">i2pd</a>.</p>
|
||||
<p>By default, I2P will encrypt and mix all traffic sent over the Internet, and
|
||||
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
|
||||
will also relay other I2P user’s encrypted packets, which will use extra
|
||||
bandwidth and compute power, but also makes timing attacks and other forms of
|
||||
deep-packet-inspection much more difficult.</p>
|
||||
<p>I2P also allows users to host globally available Reticulum instances from non-public IPs and behind firewalls.</p>
|
||||
<p>In general it is recommended to use an I2P node if you want to host a publically accessible
|
||||
instance, while preserving anonymity. If you care more about performance, and a slightly
|
||||
easier setup, use TCP.</p>
|
||||
</div>
|
||||
<div class="section" id="connect-to-the-public-testnet">
|
||||
<h2>Connect to the Public Testnet<a class="headerlink" href="#connect-to-the-public-testnet" title="Permalink to this headline">¶</a></h2>
|
||||
<p>An experimental public testnet has been made accessible over both I2P and TCP. You can join it
|
||||
by adding one of the following interfaces to your <code class="docutils literal notranslate"><span class="pre">.reticulum/config</span></code> file:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># For connecting over TCP/IP:</span>
|
||||
<span class="p">[[</span><span class="n">RNS</span> <span class="n">Testnet</span> <span class="n">Frankfurt</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">target_host</span> <span class="o">=</span> <span class="n">frankfurt</span><span class="o">.</span><span class="n">rns</span><span class="o">.</span><span class="n">unsigned</span><span class="o">.</span><span class="n">io</span>
|
||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4965</span>
|
||||
|
||||
|
||||
<span class="c1"># For connecting over I2P:</span>
|
||||
<span class="p">[[</span><span class="n">RNS</span> <span class="n">Testnet</span> <span class="n">I2P</span> <span class="n">Node</span> <span class="n">A</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">I2PInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">peers</span> <span class="o">=</span> <span class="n">ykzlw5ujbaqc2xkec4cpvgyxj257wcrmmgkuxqmqcur7cq3w3lha</span><span class="o">.</span><span class="n">b32</span><span class="o">.</span><span class="n">i2p</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Many other Reticulum instances are connecting to this testnet, and you can also join it
|
||||
via other entry points if you know them. There is absolutely no control over the network
|
||||
topography, usage or what types of instances connect. It will also occasionally be used
|
||||
to test various failure scenarios, and there are no availability or service guarantees.</p>
|
||||
</div>
|
||||
<div class="section" id="develop-a-program-with-reticulum">
|
||||
<h2>Develop a Program with Reticulum<a class="headerlink" href="#develop-a-program-with-reticulum" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If you want to develop programs that use Reticulum, the easiest way to get
|
||||
@@ -165,8 +258,10 @@ installing Reticulum or programs that depend on Reticulum.</p>
|
||||
<div class="section" id="reticulum-on-android">
|
||||
<h2>Reticulum on Android<a class="headerlink" href="#reticulum-on-android" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Reticulum can be used on Android in different ways. The easiest way to get
|
||||
started is using the <a class="reference external" href="https://termux.com/">Termux app</a>, at the time of writing
|
||||
available on <a class="reference external" href="https://f-droid.org">F-droid</a>.</p>
|
||||
started is using an app like <a class="reference external" href="https://unsigned.io/sideband">Sideband</a>.</p>
|
||||
<p>For more control and features, you can use Reticulum and related programs via
|
||||
the <a class="reference external" href="https://termux.com/">Termux app</a>, at the time of writing available on
|
||||
<a class="reference external" href="https://f-droid.org">F-droid</a>.</p>
|
||||
<p>Termux is a terminal emulator and Linux environment for Android based devices,
|
||||
which includes the ability to use many different programs and libraries,
|
||||
including Reticulum.</p>
|
||||
@@ -202,6 +297,24 @@ and a few extra commands are required.</p>
|
||||
Android APKs. A detailed tutorial and example source code will be included
|
||||
here at a later point.</p>
|
||||
</div>
|
||||
<div class="section" id="adding-radio-interfaces">
|
||||
<h2>Adding Radio Interfaces<a class="headerlink" href="#adding-radio-interfaces" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Once you have Reticulum installed and working, you can add radio interfaces with
|
||||
any compatible hardware you have available. For information on how to configure
|
||||
this, see the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Interfaces</span></a> section of this manual.</p>
|
||||
<p>A range of common LoRa development boards and transceiver modules can be used
|
||||
as interfaces with Reticulum. You can refer to the following external resources
|
||||
for more information:</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference external" href="https://unsigned.io/how-to-make-your-own-rnodes/">How To Make Your Own RNodes</a></p></li>
|
||||
<li><p><a class="reference external" href="https://unsigned.io/installing-rnode-firmware-on-t-beam-and-lora32-devices/">Installing RNode Firmware on Compatible LoRa Devices</a></p></li>
|
||||
<li><p><a class="reference external" href="https://unsigned.io/private-messaging-over-lora/">Private, Secure and Uncensorable Messaging Over a LoRa Mesh</a></p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/markqvist/RNode_Firmware/">RNode Firmware</a></p></li>
|
||||
</ul>
|
||||
<p>If you have communications hardware that you think would be suitable for use with Reticulum,
|
||||
you are welcome to head over to the <a class="reference external" href="https://github.com/markqvist/Reticulum/discussions">GitHub discussion pages</a>
|
||||
and propose adding an interface for the hardware.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -214,13 +327,20 @@ here at a later point.</p>
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Getting Started Fast</a><ul>
|
||||
<li><a class="reference internal" href="#try-using-a-reticulum-based-program">Try Using a Reticulum-based Program</a></li>
|
||||
<li><a class="reference internal" href="#try-using-a-reticulum-based-program">Try Using a Reticulum-based Program</a><ul>
|
||||
<li><a class="reference internal" href="#nomad-network">Nomad Network</a></li>
|
||||
<li><a class="reference internal" href="#sideband">Sideband</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#using-the-included-utilities">Using the Included Utilities</a></li>
|
||||
<li><a class="reference internal" href="#creating-a-network-with-reticulum">Creating a Network With Reticulum</a></li>
|
||||
<li><a class="reference internal" href="#connecting-reticulum-instances-over-the-internet">Connecting Reticulum Instances Over the Internet</a></li>
|
||||
<li><a class="reference internal" href="#connect-to-the-public-testnet">Connect to the Public Testnet</a></li>
|
||||
<li><a class="reference internal" href="#develop-a-program-with-reticulum">Develop a Program with Reticulum</a></li>
|
||||
<li><a class="reference internal" href="#participate-in-reticulum-development">Participate in Reticulum Development</a></li>
|
||||
<li><a class="reference internal" href="#reticulum-on-arm64">Reticulum on ARM64</a></li>
|
||||
<li><a class="reference internal" href="#reticulum-on-android">Reticulum on Android</a></li>
|
||||
<li><a class="reference internal" href="#adding-radio-interfaces">Adding Radio Interfaces</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -264,7 +384,7 @@ here at a later point.</p>
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+18
-8
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Reticulum Network Stack Manual — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Reticulum Network Stack Manual — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -53,13 +53,20 @@ to participate in the development of Reticulum itself.</p>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="gettingstartedfast.html">Getting Started Fast</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#try-using-a-reticulum-based-program">Try Using a Reticulum-based Program</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#try-using-a-reticulum-based-program">Try Using a Reticulum-based Program</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#nomad-network">Nomad Network</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#sideband">Sideband</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#using-the-included-utilities">Using the Included Utilities</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#creating-a-network-with-reticulum">Creating a Network With Reticulum</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#connecting-reticulum-instances-over-the-internet">Connecting Reticulum Instances Over the Internet</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#connect-to-the-public-testnet">Connect to the Public Testnet</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#develop-a-program-with-reticulum">Develop a Program with Reticulum</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#participate-in-reticulum-development">Participate in Reticulum Development</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#reticulum-on-arm64">Reticulum on ARM64</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#reticulum-on-android">Reticulum on Android</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#adding-radio-interfaces">Adding Radio Interfaces</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a><ul>
|
||||
@@ -88,10 +95,12 @@ to participate in the development of Reticulum itself.</p>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#common-interface-options">Common Interface Options</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#auto-interface">Auto Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#udp-interface">UDP Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#i2p-interface">I2P Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#tcp-server-interface">TCP Server Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#tcp-client-interface">TCP Client Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#udp-interface">UDP Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#rnode-lora-interface">RNode LoRa Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#serial-interface">Serial Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#kiss-interface">KISS Interface</a></li>
|
||||
@@ -109,16 +118,17 @@ to participate in the development of Reticulum itself.</p>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reticulum-transport">Reticulum Transport</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#node-types">Node Types</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#the-announce-mechanism-in-detail">The Announce Mechanism in Detail</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#reaching-the-destination">Reaching the Destination</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#resources">Resources</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reference-system-setup">Reference System Setup</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reference-setup">Reference Setup</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="understanding.html#protocol-specifics">Protocol Specifics</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#node-types">Node Types</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#packet-prioritisation">Packet Prioritisation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#binary-packet-format">Binary Packet Format</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#interface-access-codes">Interface Access Codes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#wire-format">Wire Format</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -208,7 +218,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
>next</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+229
-93
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Supported Interfaces — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Supported Interfaces — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="networks.html" title="Building Networks"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Supported Interfaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -53,6 +53,88 @@ and gives example configurations for the respective interface types.</p>
|
||||
<p>For a high-level overview of how networks can be formed over different interface
|
||||
types, have a look at the <a class="reference internal" href="networks.html#networks-main"><span class="std std-ref">Building Networks</span></a> chapter of this
|
||||
manual.</p>
|
||||
<div class="section" id="common-interface-options">
|
||||
<span id="interfaces-options"></span><h2>Common Interface Options<a class="headerlink" href="#common-interface-options" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.</p>
|
||||
<blockquote>
|
||||
<div><ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">enabled</span></code> option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to <code class="docutils literal notranslate"><span class="pre">False</span></code>. For any
|
||||
interface to be brought up, the <code class="docutils literal notranslate"><span class="pre">enabled</span></code> option
|
||||
must be set to <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">Yes</span></code>.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">mode</span></code> option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><ul class="simple">
|
||||
<li><p>The default value is <code class="docutils literal notranslate"><span class="pre">full</span></code>. In this mode, all discovery,
|
||||
meshing and transport functionality is available.</p></li>
|
||||
<li><p>In the <code class="docutils literal notranslate"><span class="pre">access_point</span></code> (or shorthand <code class="docutils literal notranslate"><span class="pre">ap</span></code>) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.</p></li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">outgoing</span></code> option sets whether an interface is allowed
|
||||
to transmit. Defaults to <code class="docutils literal notranslate"><span class="pre">True</span></code>. If set to <code class="docutils literal notranslate"><span class="pre">False</span></code> or <code class="docutils literal notranslate"><span class="pre">No</span></code>
|
||||
the interface will only receive data, and never transmit.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">network_name</span></code> option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">passphrase</span></code> option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
<code class="docutils literal notranslate"><span class="pre">network_name</span></code> option, or be used alone.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ifac_size</span></code> option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">announce_cap</span></code> option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between <code class="docutils literal notranslate"><span class="pre">1</span></code> and <code class="docutils literal notranslate"><span class="pre">100</span></code>.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">bitrate</span></code> option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn’t report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the <code class="docutils literal notranslate"><span class="pre">bitrate</span></code>
|
||||
option, to set the interface speed in <em>bits per second</em>.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</div>
|
||||
<div class="section" id="auto-interface">
|
||||
<span id="interfaces-auto"></span><h2>Auto Interface<a class="headerlink" href="#auto-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The Auto Interface enables communication with other discoverable Reticulum
|
||||
@@ -68,7 +150,6 @@ system, which should be enabled by default in almost all OSes.</p>
|
||||
<span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># You can create multiple isolated Reticulum</span>
|
||||
<span class="c1"># networks on the same physical LAN by</span>
|
||||
@@ -95,7 +176,6 @@ the discovery scope by setting it to one of <code class="docutils literal notran
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># Configure global discovery</span>
|
||||
|
||||
@@ -108,8 +188,146 @@ the discovery scope by setting it to one of <code class="docutils literal notran
|
||||
<span class="n">data_port</span> <span class="o">=</span> <span class="mi">49555</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><em>Please Note!</em> If you use the Auto Interface, you will need the Python module
|
||||
<code class="docutils literal notranslate"><span class="pre">netifaces</span></code> installed on your system. You can install it with <code class="docutils literal notranslate"><span class="pre">pip3</span> <span class="pre">install</span> <span class="pre">netifaces</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="i2p-interface">
|
||||
<span id="interfaces-i2p"></span><h2>I2P Interface<a class="headerlink" href="#i2p-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The I2P interface lets you connect Reticulum instances over the
|
||||
<a class="reference external" href="https://i2pd.website">Invisible Internet Protocol</a>. This can be
|
||||
especially useful in cases where you want to host a globally reachable
|
||||
Reticulum instance, but do not have access to any public IP addresses,
|
||||
have a frequently changing IP address, or have firewalls blocking
|
||||
inbound traffic.</p>
|
||||
<p>Using the I2P interface, you will get a globally reachable, portable
|
||||
and persistent I2P address that your Reticulum instance can be reached
|
||||
at.</p>
|
||||
<p>To use the I2P interface, you must have an I2P router running
|
||||
on your system. The easiest way to acheive this is to download and
|
||||
install the <a class="reference external" href="https://github.com/PurpleI2P/i2pd/releases/latest">latest release</a>
|
||||
of the <code class="docutils literal notranslate"><span class="pre">ì2pd</span></code> package. For more details about I2P, see the
|
||||
<a class="reference external" href="https://geti2p.net/en/about/intro">geti2p.net website</a>.`</p>
|
||||
<p>When an I2P router is running on your system, you can simply add
|
||||
an I2P interface to reticulum:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">I2P</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">I2PInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">connectable</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>On the first start, Reticulum will generate a new I2P address for the
|
||||
interface and start listening for inbound traffic on it. This can take
|
||||
a while the first time, especially if your I2P router was also just
|
||||
started, and is not yet well-connected to the I2P network. When ready,
|
||||
you should see I2P base32 address printed to your log file. You can
|
||||
also inspect the status of the interface using the <code class="docutils literal notranslate"><span class="pre">rnstatus</span></code> utility.</p>
|
||||
<p>To connect to other Reticulum instances over I2P, just add a comma-separated
|
||||
list of I2P base32 addresses to the <code class="docutils literal notranslate"><span class="pre">peers</span></code> option of the interface:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">I2P</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">I2PInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">connectable</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">peers</span> <span class="o">=</span> <span class="mi">5</span><span class="n">urvjicpzi7q3ybztsef4i5ow2aq4soktfj7zedz53s47r54jnqq</span><span class="o">.</span><span class="n">b32</span><span class="o">.</span><span class="n">i2p</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It can take anywhere from a few seconds to a few minutes to establish
|
||||
I2P connections to the desired peers, so Reticulum handles the process
|
||||
in the background, and will output relevant events to the log.</p>
|
||||
<p><strong>Please Note!</strong> While the I2P interface is the simplest way to use
|
||||
Reticulum over I2P, it is also possible to tunnel the TCP server and
|
||||
client interfaces over I2P manually. This can be useful in situations
|
||||
where more control is needed, but requires manual tunnel setup through
|
||||
the I2P daemon configuration.</p>
|
||||
<p>It is important to note that the two methods are <em>interchangably compatible</em>.
|
||||
You can use the I2PInterface to connect to a TCPServerInterface that
|
||||
was manually tunneled over I2P, for example. This offers a high degree
|
||||
of flexibility in network setup, while retaining ease of use in simpler
|
||||
use-cases.</p>
|
||||
</div>
|
||||
<div class="section" id="tcp-server-interface">
|
||||
<span id="interfaces-tcps"></span><h2>TCP Server Interface<a class="headerlink" href="#tcp-server-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The TCP Server interface is suitable for allowing other peers to connect over
|
||||
the Internet or private IP networks. When a TCP server interface has been
|
||||
configured, other Reticulum peers can connect to it with a TCP Client interface.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example demonstrates a TCP server interface.</span>
|
||||
<span class="c1"># It will listen for incoming connections on the</span>
|
||||
<span class="c1"># specified IP address and port number.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">TCP</span> <span class="n">Server</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPServerInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># This configuration will listen on all IP</span>
|
||||
<span class="c1"># interfaces on port 4242</span>
|
||||
|
||||
<span class="n">listen_ip</span> <span class="o">=</span> <span class="mf">0.0</span><span class="o">.</span><span class="mf">0.0</span>
|
||||
<span class="n">listen_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
||||
|
||||
<span class="c1"># Alternatively you can bind to a specific IP</span>
|
||||
|
||||
<span class="c1"># listen_ip = 10.0.0.88</span>
|
||||
<span class="c1"># listen_port = 4242</span>
|
||||
|
||||
<span class="c1"># Or a specific network device</span>
|
||||
|
||||
<span class="c1"># device = eth0</span>
|
||||
<span class="c1"># port = 4242</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><strong>Please Note!</strong> The TCP interfaces support tunneling over I2P, but to do so reliably,
|
||||
you must use the i2p_tunneled option:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">TCP</span> <span class="n">Server</span> <span class="n">on</span> <span class="n">I2P</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPServerInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">listen_ip</span> <span class="o">=</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span>
|
||||
<span class="n">listen_port</span> <span class="o">=</span> <span class="mi">5001</span>
|
||||
<span class="n">i2p_tunneled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="tcp-client-interface">
|
||||
<span id="interfaces-tcpc"></span><h2>TCP Client Interface<a class="headerlink" href="#tcp-client-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here's an example of a TCP Client interface. The</span>
|
||||
<span class="c1"># target_host can either be an IP address or a hostname.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">TCP</span> <span class="n">Client</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">target_host</span> <span class="o">=</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span>
|
||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It is also possible to use this interface type to connect via other programs
|
||||
or hardware devices that expose a KISS interface on a TCP port, for example
|
||||
software-based soundmodems. To do this, use the <code class="docutils literal notranslate"><span class="pre">kiss_framing</span></code> option:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here's an example of a TCP Client interface that connects</span>
|
||||
<span class="c1"># to a software TNC soundmodem on a KISS over TCP port.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">TCP</span> <span class="n">KISS</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">kiss_framing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">target_host</span> <span class="o">=</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span>
|
||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">8001</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><strong>Caution!</strong> Only use the KISS framing option when connecting to external devices
|
||||
and programs like soundmodems and similar over TCP. When using the
|
||||
<code class="docutils literal notranslate"><span class="pre">TCPClientInterface</span></code> in conjunction with the <code class="docutils literal notranslate"><span class="pre">TCPServerInterface</span></code> you should
|
||||
never enable <code class="docutils literal notranslate"><span class="pre">kiss_framing</span></code>, since this will disable internal reliability and
|
||||
recovery mechanisms that greatly improves performance over unreliable and
|
||||
intermittent TCP links.</p>
|
||||
<p><strong>Please Note!</strong> The TCP interfaces support tunneling over I2P, but to do so reliably,
|
||||
you must use the i2p_tunneled option:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">TCP</span> <span class="n">Client</span> <span class="n">over</span> <span class="n">I2P</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
<span class="n">target_host</span> <span class="o">=</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span>
|
||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">5001</span>
|
||||
<span class="n">i2p_tunneled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="udp-interface">
|
||||
<span id="interfaces-udp"></span><h2>UDP Interface<a class="headerlink" href="#udp-interface" title="Permalink to this headline">¶</a></h2>
|
||||
@@ -131,7 +349,7 @@ pre-existing LAN.</p>
|
||||
<span class="p">[[</span><span class="n">Default</span> <span class="n">UDP</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">UDPInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="n">listen_ip</span> <span class="o">=</span> <span class="mf">0.0</span><span class="o">.</span><span class="mf">0.0</span>
|
||||
<span class="n">listen_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
||||
<span class="n">forward_ip</span> <span class="o">=</span> <span class="mf">255.255</span><span class="o">.</span><span class="mf">255.255</span>
|
||||
@@ -168,80 +386,6 @@ pre-existing LAN.</p>
|
||||
<span class="c1"># forward_port = 4242</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><em>Please Note!</em> If you use the <code class="docutils literal notranslate"><span class="pre">device</span></code> option, you will need the Python module
|
||||
<code class="docutils literal notranslate"><span class="pre">netifaces</span></code> installed on your system. You can install it with <code class="docutils literal notranslate"><span class="pre">pip3</span> <span class="pre">install</span> <span class="pre">netifaces</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="tcp-server-interface">
|
||||
<span id="interfaces-tcps"></span><h2>TCP Server Interface<a class="headerlink" href="#tcp-server-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The TCP Server interface is suitable for allowing other peers to connect over
|
||||
the Internet or private IP networks. When a TCP server interface has been
|
||||
configured, other Reticulum peers can connect to it with a TCP Client interface.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example demonstrates a TCP server interface.</span>
|
||||
<span class="c1"># It will listen for incoming connections on the</span>
|
||||
<span class="c1"># specified IP address and port number.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">TCP</span> <span class="n">Server</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPServerInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># This configuration will listen on all IP</span>
|
||||
<span class="c1"># interfaces on port 4242</span>
|
||||
|
||||
<span class="n">listen_ip</span> <span class="o">=</span> <span class="mf">0.0</span><span class="o">.</span><span class="mf">0.0</span>
|
||||
<span class="n">listen_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
||||
|
||||
<span class="c1"># Alternatively you can bind to a specific IP</span>
|
||||
|
||||
<span class="c1"># listen_ip = 10.0.0.88</span>
|
||||
<span class="c1"># listen_port = 4242</span>
|
||||
|
||||
<span class="c1"># Or a specific network device</span>
|
||||
|
||||
<span class="c1"># device = eth0</span>
|
||||
<span class="c1"># port = 4242</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><em>Please Note!</em> If you use the <code class="docutils literal notranslate"><span class="pre">device</span></code> option, you will need the Python module
|
||||
<code class="docutils literal notranslate"><span class="pre">netifaces</span></code> installed on your system. You can install it with <code class="docutils literal notranslate"><span class="pre">pip3</span> <span class="pre">install</span> <span class="pre">netifaces</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="tcp-client-interface">
|
||||
<span id="interfaces-tcpc"></span><h2>TCP Client Interface<a class="headerlink" href="#tcp-client-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here's an example of a TCP Client interface. The</span>
|
||||
<span class="c1"># target_host can either be an IP address or a hostname.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">TCP</span> <span class="n">Client</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">target_host</span> <span class="o">=</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span>
|
||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It is also possible to use this interface type to connect via other programs
|
||||
or hardware devices that expose a KISS interface on a TCP port, for example
|
||||
software-based soundmodems. To do this, use the <code class="docutils literal notranslate"><span class="pre">kiss_framing</span></code> option:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here's an example of a TCP Client interface that connects</span>
|
||||
<span class="c1"># to a software TNC soundmodem on a KISS over TCP port.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">TCP</span> <span class="n">KISS</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">kiss_framing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">target_host</span> <span class="o">=</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span>
|
||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">8001</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><strong>Caution!</strong> Only use the KISS framing option when connecting to external devices
|
||||
and programs like soundmodems and similar over TCP. When using the
|
||||
<code class="docutils literal notranslate"><span class="pre">TCPClientInterface</span></code> in conjunction with the <code class="docutils literal notranslate"><span class="pre">TCPServerInterface</span></code> you should
|
||||
never enable <code class="docutils literal notranslate"><span class="pre">kiss_framing</span></code>, since this will disable internal reliability and
|
||||
recovery mechanisms that greatly improves performance over unreliable and
|
||||
intermittent TCP links.</p>
|
||||
</div>
|
||||
<div class="section" id="rnode-lora-interface">
|
||||
<span id="interfaces-rnode"></span><h2>RNode LoRa Interface<a class="headerlink" href="#rnode-lora-interface" title="Permalink to this headline">¶</a></h2>
|
||||
@@ -256,11 +400,6 @@ can be used, and offers full control over LoRa parameters.</p>
|
||||
<span class="c1"># Enable interface if you want use it!</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># Allow transmit on interface. Setting</span>
|
||||
<span class="c1"># this to false will create a listen-</span>
|
||||
<span class="c1"># only interface.</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="n">true</span>
|
||||
|
||||
<span class="c1"># Serial port for the device</span>
|
||||
<span class="n">port</span> <span class="o">=</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB0</span>
|
||||
|
||||
@@ -307,7 +446,6 @@ directly over a wire-pair, or for using devices such as data radios and lasers.<
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">Serial</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">SerialInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># Serial port for the device</span>
|
||||
<span class="n">port</span> <span class="o">=</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB0</span>
|
||||
@@ -330,7 +468,6 @@ for station identification purposes.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">Packet</span> <span class="n">Radio</span> <span class="n">KISS</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">KISSInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="n">true</span>
|
||||
|
||||
<span class="c1"># Serial port for the device</span>
|
||||
<span class="n">port</span> <span class="o">=</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB1</span>
|
||||
@@ -395,9 +532,6 @@ beaconing functionality described above.</p>
|
||||
<span class="c1"># Enable interface if you want use it!</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># Allow transmit on interface.</span>
|
||||
<span class="n">outgoing</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># Serial port for the device</span>
|
||||
<span class="n">port</span> <span class="o">=</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB2</span>
|
||||
|
||||
@@ -445,10 +579,12 @@ beaconing functionality described above.</p>
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Supported Interfaces</a><ul>
|
||||
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
|
||||
<li><a class="reference internal" href="#auto-interface">Auto Interface</a></li>
|
||||
<li><a class="reference internal" href="#udp-interface">UDP Interface</a></li>
|
||||
<li><a class="reference internal" href="#i2p-interface">I2P Interface</a></li>
|
||||
<li><a class="reference internal" href="#tcp-server-interface">TCP Server Interface</a></li>
|
||||
<li><a class="reference internal" href="#tcp-client-interface">TCP Client Interface</a></li>
|
||||
<li><a class="reference internal" href="#udp-interface">UDP Interface</a></li>
|
||||
<li><a class="reference internal" href="#rnode-lora-interface">RNode LoRa Interface</a></li>
|
||||
<li><a class="reference internal" href="#serial-interface">Serial Interface</a></li>
|
||||
<li><a class="reference internal" href="#kiss-interface">KISS Interface</a></li>
|
||||
@@ -496,7 +632,7 @@ beaconing functionality described above.</p>
|
||||
<li class="right" >
|
||||
<a href="networks.html" title="Building Networks"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Supported Interfaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Building Networks — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Building Networks — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="using.html" title="Using Reticulum on Your System"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Building Networks</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -70,6 +70,13 @@ also very useful when just a few devices needs to communicate.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">Low-bandwidth networks, like LoRa and packet radio, can interoperate and
|
||||
interconnect with much larger and higher bandwidth networks without issue.
|
||||
Reticulum automatically manages the flow of information to and from various
|
||||
network segments, and when bandwidth is limited, local traffic is prioritised.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">Reticulum provides sender/initiator anonymity by default. There is no way
|
||||
to filter traffic or discriminate it based on the source of the traffic.</div>
|
||||
</div>
|
||||
@@ -97,20 +104,38 @@ transport node. Letting every node be a transport node will in most cases
|
||||
degrade the performance and reliability of the network.</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>In general terms, if a node is stationary, well-connected and kept running
|
||||
<div><p><em>In general terms, if a node is stationary, well-connected and kept running
|
||||
most of the time, it is a good candidate to be a transport node. For optimal
|
||||
performance, a network should contain the amount of transport nodes that
|
||||
provides connectivity to the intended area / topography, and not many more
|
||||
than that.</p>
|
||||
than that.</em></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">Reticulum is designed to work reliably in open, trustless environments. This
|
||||
means you can use it to create open-access networks, where participants can
|
||||
join and leave in an free and unorganised manner. This property allows an
|
||||
entirely new, and so far, mostly unexplored class of networked applications,
|
||||
where networks, and the information flow within them can form and dissolve
|
||||
organically.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">You can just as easily create closed networks, since Reticulum allows you to
|
||||
add authentication to any interface. This means you can restrict access on
|
||||
any interface type, even when using legacy devices, such as modems. You can
|
||||
also mix authenticated and open interfaces on the same system. See the
|
||||
<a class="reference internal" href="interfaces.html#interfaces-options"><span class="std std-ref">Common Interface Options</span></a> section of the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Interfaces</span></a>
|
||||
chapter of this manual for information on how to set up interface authentication.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
<p>Reticulum allows you to mix very different kinds of networking mediums into a
|
||||
unified mesh, or to keep everything within one medium. You could build a “virtual
|
||||
network” running entirely over the Internet, where all nodes communicate over TCP
|
||||
and UDP “channels”. You could also build such a network using MQTT or ZeroMQ as
|
||||
the underlying carrier for Reticulum.</p>
|
||||
and UDP “channels”. You could also build such a network using other already-established
|
||||
communications channels as the underlying carrier for Reticulum.</p>
|
||||
<p>However, most real-world networks will probably involve either some form of
|
||||
wireless or direct hardline communications. To allow Reticulum to communicate
|
||||
over any type of medium, you must specify it in the configuration file, by default
|
||||
@@ -247,7 +272,7 @@ connected outliers are now an integral part of the network.</p>
|
||||
<li class="right" >
|
||||
<a href="using.html" title="Using Reticulum on Your System"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Building Networks</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>API Reference — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>API Reference — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="understanding.html" title="Understanding Reticulum"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -76,7 +76,7 @@ other programs to use on demand.</p>
|
||||
<dt class="sig sig-object py" id="RNS.Reticulum.MTU">
|
||||
<span class="sig-name descname"><span class="pre">MTU</span></span><em class="property"> <span class="pre">=</span> <span class="pre">500</span></em><a class="headerlink" href="#RNS.Reticulum.MTU" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The MTU that Reticulum adheres to, and will expect other peers to
|
||||
adhere to. By default, the MTU is 500 bytes. In custom RNS network
|
||||
adhere to. By default, the MTU is 507 bytes. In custom RNS network
|
||||
implementations, it is possible to change this value, but doing so will
|
||||
completely break compatibility with all other RNS networks. An identical
|
||||
MTU is a prerequisite for peers to communicate in the same network.</p>
|
||||
@@ -84,6 +84,23 @@ MTU is a prerequisite for peers to communicate in the same network.</p>
|
||||
the default value.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="RNS.Reticulum.ANNOUNCE_CAP">
|
||||
<span class="sig-name descname"><span class="pre">ANNOUNCE_CAP</span></span><em class="property"> <span class="pre">=</span> <span class="pre">2</span></em><a class="headerlink" href="#RNS.Reticulum.ANNOUNCE_CAP" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The maximum percentage of interface bandwidth that, at any given time,
|
||||
may be used to propagate announces. If an announce was scheduled for
|
||||
broadcasting on an interface, but doing so would exceed the allowed
|
||||
bandwidth allocation, the announce will be queued for transmission
|
||||
when there is bandwidth available.</p>
|
||||
<p>Reticulum will always prioritise propagating announces with fewer
|
||||
hops, ensuring that distant, large networks with many peers on fast
|
||||
links don’t overwhelm the capacity of smaller networks on slower
|
||||
mediums. If an announce remains queued for an extended amount of time,
|
||||
it will eventually be dropped.</p>
|
||||
<p>This value will be applied by default to all created interfaces,
|
||||
but it can be configured individually on a per-interface basis.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Reticulum.should_use_implicit_proof">
|
||||
<em class="property"><span class="pre">static</span> </em><span class="sig-name descname"><span class="pre">should_use_implicit_proof</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Reticulum.should_use_implicit_proof" title="Permalink to this definition">¶</a></dt>
|
||||
@@ -671,7 +688,7 @@ destinations, reticulum will use ephemeral keys, and offers <strong>Forward Secr
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="RNS.Packet.PLAIN_MDU">
|
||||
<span class="sig-name descname"><span class="pre">PLAIN_MDU</span></span><em class="property"> <span class="pre">=</span> <span class="pre">477</span></em><a class="headerlink" href="#RNS.Packet.PLAIN_MDU" title="Permalink to this definition">¶</a></dt>
|
||||
<span class="sig-name descname"><span class="pre">PLAIN_MDU</span></span><em class="property"> <span class="pre">=</span> <span class="pre">476</span></em><a class="headerlink" href="#RNS.Packet.PLAIN_MDU" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The maximum size of the payload data in a single unencrypted packet</p>
|
||||
</dd></dl>
|
||||
|
||||
@@ -1156,13 +1173,16 @@ Transport system of Reticulum.</p>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Transport.request_path">
|
||||
<em class="property"><span class="pre">static</span> </em><span class="sig-name descname"><span class="pre">request_path</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination_hash</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Transport.request_path" title="Permalink to this definition">¶</a></dt>
|
||||
<em class="property"><span class="pre">static</span> </em><span class="sig-name descname"><span class="pre">request_path</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination_hash</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">on_interface</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Transport.request_path" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Requests a path to the destination from the network. If
|
||||
another reachable peer on the network knows a path, it
|
||||
will announce it.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>destination_hash</strong> – A destination hash as <em>bytes</em>.</p>
|
||||
<dd class="field-odd"><ul class="simple">
|
||||
<li><p><strong>destination_hash</strong> – A destination hash as <em>bytes</em>.</p></li>
|
||||
<li><p><strong>on_interface</strong> – If specified, the path request will only be sent on this interface. In normal use, Reticulum handles this automatically, and this parameter should not be used.</p></li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -1238,7 +1258,7 @@ will announce it.</p>
|
||||
<li class="right" >
|
||||
<a href="understanding.html" title="Understanding Reticulum"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Search — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Search — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -85,7 +85,7 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
File diff suppressed because one or more lines are too long
+262
-175
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Understanding Reticulum — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Understanding Reticulum — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="interfaces.html" title="Supported Interfaces"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -43,44 +43,50 @@
|
||||
|
||||
<div class="section" id="understanding-reticulum">
|
||||
<span id="understanding-main"></span><h1>Understanding Reticulum<a class="headerlink" href="#understanding-reticulum" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This chapter will briefly describe the overall purpose and operating principles of Reticulum, a
|
||||
networking stack designed for reliable and secure communication over high-latency, low-bandwidth
|
||||
links. It should give you an overview of how the stack works, and an understanding of how to
|
||||
<p>This chapter will briefly describe the overall purpose and operating principles of Reticulum.
|
||||
It should give you an overview of how the stack works, and an understanding of how to
|
||||
develop networked applications using Reticulum.</p>
|
||||
<p>This document is not an exhaustive source of information on Reticulum, at least not yet. Currently,
|
||||
the best place to go for such information is the Python reference implementation of Reticulum, along
|
||||
with the code examples and API reference. It is however an essential resource to understanding the
|
||||
general principles of Reticulum, how to apply them when creating your own networks or software.</p>
|
||||
<p>This chapter is not an exhaustive source of information on Reticulum, at least not yet. Currently,
|
||||
the only complete repository, and final authority on how Reticulum actually functions, is the Python
|
||||
reference implementation and API reference. That being said, this chapter is an essential resource in
|
||||
understanding how Reticulum works from a high-level perspective, along with the general principles of
|
||||
Reticulum, and how to apply them when creating your own networks or software.</p>
|
||||
<p>After reading this document, you should be well-equipped to understand how a Reticulum network
|
||||
operates, what it can achieve, and how you can use it yourself. If you want to help out with the
|
||||
development, this is also the place to start, since it will provide a pretty clear overview of the
|
||||
sentiments and the philosophy behind Reticulum.</p>
|
||||
sentiments and the philosophy behind Reticulum, what problems it seeks to solve, and how it
|
||||
approaches those solutions.</p>
|
||||
<div class="section" id="motivation">
|
||||
<span id="understanding-motivation"></span><h2>Motivation<a class="headerlink" href="#motivation" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The primary motivation for designing and implementing Reticulum has been the current lack of
|
||||
reliable, functional and secure minimal-infrastructure modes of digital communication. It is my
|
||||
belief that it is highly desirable to create a cheap and reliable way to set up a wide-range digital
|
||||
communication network that can securely allow exchange of information between people and
|
||||
belief that it is highly desirable to create a reliable and efficient way to set up long-range digital
|
||||
communication networks that can securely allow exchange of information between people and
|
||||
machines, with no central point of authority, control, censorship or barrier to entry.</p>
|
||||
<p>Almost all of the various networking systems in use today share a common limitation, namely that they
|
||||
require large amounts of coordination and trust to work, and to join the networks you need approval
|
||||
<p>Almost all of the various networking systems in use today share a common limitation: They
|
||||
require large amounts of coordination and centralised trust and power to function. To join such networks, you need approval
|
||||
of gatekeepers in control. This need for coordination and trust inevitably leads to an environment of
|
||||
central control, where it’s very easy for infrastructure operators or governments to control or alter
|
||||
traffic, and censor or persecute unwanted actors.</p>
|
||||
<p>Reticulum aims to require as little coordination and trust as possible. In fact, the only
|
||||
“coordination” required is to know the characteristics of physical medium carrying Reticulum traffic.</p>
|
||||
<p>Since Reticulum is completely medium agnostic, this could be whatever is best suited to the situation.
|
||||
In some cases, this might be 1200 baud packet radio links over VHF frequencies, in other cases it might
|
||||
be a microwave network using off-the-shelf radios. At the time of release of this document, the
|
||||
recommended setup for development and testing is using LoRa radio modules with an open source firmware
|
||||
(see the section <a class="reference internal" href="#understanding-referencesystem"><span class="std std-ref">Reference System Setup</span></a>), connected to a small
|
||||
computer like a Raspberry Pi. As an example, the default reference setup provides a channel capacity
|
||||
of 5.4 Kbps, and a usable direct node-to-node range of around 15 kilometers (indefinitely extendable
|
||||
by using multiple hops).</p>
|
||||
traffic, and censor or persecute unwanted actors. It also makes it completely impossible to freely deploy
|
||||
and use networks at will, like one would use other common tools that enhance individual agency and freedom.</p>
|
||||
<p>Reticulum aims to require as little coordination and trust as possible. It aims to make secure,
|
||||
anonymous and permissionless networking and information exchange a tool that anyone can just pick up and use.</p>
|
||||
<p>Since Reticulum is completely medium agnostic, it can be used to build networks on whatever is best
|
||||
suited to the situation, or whatever you have available. In some cases, this might be packet radio
|
||||
links over VHF frequencies, in other cases it might be a 2.4 GHz
|
||||
network using off-the-shelf radios, or it might be using common LoRa development boards.</p>
|
||||
<p>At the time of release of this document, the fastest and easiest setup for development and testing is using
|
||||
LoRa radio modules with an open source firmware (see the section <a class="reference internal" href="#understanding-referencesystem"><span class="std std-ref">Reference Setup</span></a>),
|
||||
connected to any kind of computer or mobile device that Reticulum can run on.</p>
|
||||
<p>The ultimate aim of Reticulum is to allow anyone to be their own network operator, and to make it
|
||||
cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks.
|
||||
Reticulum <strong>is not</strong> <em>one network</em>, it <strong>is a tool</strong> to build <em>thousands of networks</em>.</p>
|
||||
<p>Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
with each other, and require no central oversight. Networks for human beings. <em>Networks for the people</em>.</p>
|
||||
</div>
|
||||
<div class="section" id="goals">
|
||||
<span id="understanding-goals"></span><h2>Goals<a class="headerlink" href="#goals" title="Permalink to this headline">¶</a></h2>
|
||||
<p>To be as widely usable and easy to use as possible, the following goals have been used to
|
||||
<p>To be as widely usable and efficient to deploy as possible, the following goals have been used to
|
||||
guide the design of Reticulum:</p>
|
||||
<ul class="simple">
|
||||
<li><dl class="simple">
|
||||
@@ -90,17 +96,17 @@ critical to ensuring the availability, security and transparency of the system.<
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Hardware layer agnosticism</strong></dt><dd><p>Reticulum shall be fully hardware agnostic, and shall be useable over a wide range
|
||||
<dt><strong>Hardware layer agnosticism</strong></dt><dd><p>Reticulum must be fully hardware agnostic, and shall be useable over a wide range of
|
||||
physical networking layers, such as data radios, serial lines, modems, handheld transceivers,
|
||||
wired ethernet, wifi, or anything else that can carry a digital data stream. Hardware made for
|
||||
dedicated Reticulum use shall be as cheap as possible and use off-the-shelf components, so
|
||||
it can be easily replicated.</p>
|
||||
it can be easily modified and replicated by anyone interested in doing so.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Very low bandwidth requirements</strong></dt><dd><p>Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||
as <em>500 bps</em>.</p>
|
||||
as <em>500 bits per second</em>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
@@ -124,10 +130,10 @@ by connecting a modem to a PMR or CB radio, or by using LoRa or WiFi modules.</p
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Supplied software</strong></dt><dd><p>Apart from the core networking stack and API, that allows a developer to build
|
||||
applications with Reticulum, a basic communication suite using Reticulum must be
|
||||
implemented and released at the same time as Reticulum itself. This shall serve both as a
|
||||
functional communication suite, and as an example and learning resource to others wishing
|
||||
<dt><strong>Supplied software</strong></dt><dd><p>In addition to the core networking stack and API, that allows a developer to build
|
||||
applications with Reticulum, a basic set of Reticulum-based communication tools must be
|
||||
implemented and released along with Reticulum itself. These shall serve both as a
|
||||
functional, basic communication suite, and as an example and learning resource to others wishing
|
||||
to build applications with Reticulum.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -135,7 +141,7 @@ to build applications with Reticulum.</p>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Ease of use</strong></dt><dd><p>The reference implementation of Reticulum is written in Python, to make it easy to use
|
||||
and understand. A programmer with only basic experience should be able to use
|
||||
Reticulum in their own applications.</p>
|
||||
Reticulum to write networked applications.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
@@ -159,18 +165,26 @@ to be transported over multiple hops in a complex network to reach the recipient
|
||||
Reticulum uses the singular concept of <em>destinations</em>. Any application using Reticulum as it’s
|
||||
networking stack will need to create one or more destinations to receive data, and know the
|
||||
destinations it needs to send data to.</p>
|
||||
<p>All destinations in Reticulum are represented internally as 10 bytes, derived from truncating a full
|
||||
<p>All destinations in Reticulum are represented as a 10 byte hash, derived from truncating a full
|
||||
SHA-256 hash of identifying characteristics of the destination. To users, the destination addresses
|
||||
will be displayed as 10 bytes in hexadecimal representation, as in the following example: <code class="docutils literal notranslate"><span class="pre"><80e29bf7cccaf31431b3></span></code>.</p>
|
||||
<p>The truncation size of 10 bytes (80 bits) for destinations has been choosen as a reasonable tradeoff between address space
|
||||
and packet overhead. The address space accomodated by this size can support many billions of
|
||||
simultaneously active devices on the same network, while keeping packet overhead low, which is
|
||||
essential on low-bandwidth networks. In the very unlikely case that this address space nears
|
||||
congestion, a one-line code change can upgrade the Reticulum address space all the way up to 256
|
||||
bits, ensuring the Reticulum address space could potentially support galactic-scale networks.
|
||||
This is obviusly complete and ridiculous over-allocation, and as such, the current 80 bits should
|
||||
be sufficient, even far into the future.</p>
|
||||
<p>By default Reticulum encrypts all data using elliptic curve cryptography. Any packet sent to a
|
||||
destination is encrypted with a derived ephemeral key. Reticulum can also set up an encrypted
|
||||
channel to a destination with <em>Forward Secrecy</em> and <em>Initiator Anonymity</em> using a elliptic
|
||||
curve cryptography and ephemeral keys derived from a Diffie Hellman exchange on Curve25519. In
|
||||
Reticulum terminology, this is called a <em>Link</em>.</p>
|
||||
Reticulum terminology, this is called a <em>Link</em>. The multi-hop transport, coordination, verification
|
||||
and reliability layers are fully autonomous and also based on elliptic curve cryptography.</p>
|
||||
<p>Reticulum also offers symmetric key encryption for group-oriented communications, as well as
|
||||
unencrypted packets for broadcast purposes, or situations where you need the communication to be in
|
||||
plain text. The multi-hop transport, coordination, verification and reliability layers are fully
|
||||
autonomous and based on public key cryptography.</p>
|
||||
plain text.</p>
|
||||
<p>Reticulum can connect to a variety of interfaces such as radio modems, data radios and serial ports,
|
||||
and offers the possibility to easily tunnel Reticulum traffic over IP links such as the Internet or
|
||||
private IP networks.</p>
|
||||
@@ -180,23 +194,30 @@ private IP networks.</p>
|
||||
destinations. Reticulum uses three different basic destination types, and one special:</p>
|
||||
<ul class="simple">
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Single</strong></dt><dd><p>The <em>single</em> destination type is always identified by a unique public key. Any data sent to this
|
||||
<dt><strong>Single</strong></dt><dd><p>The <em>single</em> destination type is the most common type in Reticulum, and should be used for
|
||||
most purposes. It is always identified by a unique public key. Any data sent to this
|
||||
destination will be encrypted using ephemeral keys derived from an ECDH key exchange, and will
|
||||
only be readable by the creator of the destination, who holds the corresponding private key.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Group</strong></dt><dd><p>The <em>group</em> destination type defines a symmetrically encrypted destination. Data sent to this
|
||||
destination will be encrypted with a symmetric key, and will be readable by anyone in
|
||||
possession of the key.</p>
|
||||
<dt><strong>Plain</strong></dt><dd><p>A <em>plain</em> destination type is unencrypted, and suited for traffic that should be broadcast to a
|
||||
number of users, or should be readable by anyone. Traffic to a <em>plain</em> destination is not encrypted.
|
||||
Generally, <em>plain</em> destinations can be used for broadcast information intended to be public.
|
||||
Plain destinations are only reachable directly, and packets adressed to plain destinations are
|
||||
never transported over multiple hops in the network. To be transportable over multiple hops in Reticulum, information
|
||||
<em>must</em> be encrypted, since Reticulum uses the per-packet encryption to verify routing paths and
|
||||
keep them alive.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Plain</strong></dt><dd><p>A <em>plain</em> destination type is unencrypted, and suited for traffic that should be broadcast to a
|
||||
number of users, or should be readable by anyone. Traffic to a <em>plain</em> destination is not encrypted.
|
||||
Generally, <em>plain</em> destinations can be used for broadcast information intended to be public.</p>
|
||||
<dt><strong>Group</strong></dt><dd><p>The <em>group</em> special destination type, that defines a symmetrically encrypted virtual destination.
|
||||
Data sent to this destination will be encrypted with a symmetric key, and will be readable by
|
||||
anyone in possession of the key, but as with the <em>plain</em> destination type, packets to this type
|
||||
of destination are not currently transported over multiple hops, although a planned upgrade
|
||||
to Reticulum will allow globally reachable <em>group</em> destinations.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
@@ -204,7 +225,8 @@ Generally, <em>plain</em> destinations can be used for broadcast information int
|
||||
<dt><strong>Link</strong></dt><dd><p>A <em>link</em> is a special destination type, that serves as an abstract channel to a <em>single</em>
|
||||
destination, directly connected or over multiple hops. The <em>link</em> also offers reliability and
|
||||
more efficient encryption, forward secrecy, initiator anonymity, and as such can be useful even
|
||||
when a node is directly reachable.</p>
|
||||
when a node is directly reachable. It also offers a more capable API and allows easily carrying
|
||||
out requests and responses, large data transfers and more.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
@@ -240,7 +262,7 @@ addressable, because their public keys will differ.</p></li>
|
||||
<p>In actual use of <em>single</em> destination naming, it is advisable not to use any uniquely identifying
|
||||
features in aspect naming. Aspect names should be general terms describing what kind of destination
|
||||
is represented. The uniquely identifying aspect is always acheived by the appending the public key,
|
||||
which expands the destination into a uniquely identifyable one.</p>
|
||||
which expands the destination into a uniquely identifyable one. Reticulum does this automatically.</p>
|
||||
<p>Any destination on a Reticulum network can be addressed and reached simply by knowning its
|
||||
destination hash (and public key, but if the public key is not known, it can be requested from the
|
||||
network simply by knowing the destination hash). The use of app names and aspects makes it easy to
|
||||
@@ -260,7 +282,7 @@ indirectly, but must first be established through a <em>single</em> destination.
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Plain</strong></dt><dd><p>When plain-text communication is desirable, for example when broadcasting information.</p>
|
||||
<dt><strong>Plain</strong></dt><dd><p>When plain-text communication is desirable, for example when broadcasting information, or for local discovery purposes.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
@@ -268,22 +290,23 @@ indirectly, but must first be established through a <em>single</em> destination.
|
||||
<p>To communicate with a <em>single</em> destination, you need to know it’s public key. Any method for
|
||||
obtaining the public key is valid, but Reticulum includes a simple mechanism for making other
|
||||
nodes aware of your destinations public key, called the <em>announce</em>. It is also possible to request
|
||||
an unknown public key from the network, as all participating nodes serve as a distributed ledger
|
||||
an unknown public key from the network, as all transport instances serve as a distributed ledger
|
||||
of public keys.</p>
|
||||
<p>Note that public key information can be shared and verified in many other ways than using the
|
||||
built-in <em>announce</em> functionality, and that it is therefore not required to use the announce/request
|
||||
<p>Note that public key information can be shared and verified in other ways than using the
|
||||
built-in <em>announce</em> functionality, and that it is therefore not required to use the <em>announce</em> and <em>path request</em>
|
||||
functionality to obtain public keys. It is by far the easiest though, and should definitely be used
|
||||
if there is not a good reason for doing it differently.</p>
|
||||
if there is not a very good reason for doing it differently.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="public-key-announcements">
|
||||
<span id="understanding-keyannouncements"></span><h3>Public Key Announcements<a class="headerlink" href="#public-key-announcements" title="Permalink to this headline">¶</a></h3>
|
||||
<p>An <em>announce</em> will send a special packet over any configured interfaces, containing all needed
|
||||
<p>An <em>announce</em> will send a special packet over any relevant interfaces, containing all needed
|
||||
information about the destination hash and public key, and can also contain some additional,
|
||||
application specific data. The entire packet is signed by the sender to ensure authenticity. It is not
|
||||
required to use the announce functionality, but in many cases it will be the simplest way to share
|
||||
public keys on the network. As an example, an announce in a simple messenger application might
|
||||
contain the following information:</p>
|
||||
public keys on the network. The announce mechanism also serves to establish end-to-end connectivity
|
||||
to the announced destination, as the announce propagates through the network.</p>
|
||||
<p>As an example, an announce in a simple messenger application might contain the following information:</p>
|
||||
<ul class="simple">
|
||||
<li><p>The announcers destination hash</p></li>
|
||||
<li><p>The announcers public key</p></li>
|
||||
@@ -295,29 +318,37 @@ contain the following information:</p>
|
||||
destination to securely communicate with that destination. You might have noticed that there is one
|
||||
piece of information lacking to reconstruct full knowledge of the announced destination, and that is
|
||||
the aspect names of the destination. These are intentionally left out to save bandwidth, since they
|
||||
will be implicit in almost all cases. If a destination name is not entirely implicit, information can be
|
||||
included in the application specific data part that will allow the receiver to infer the naming.</p>
|
||||
will be implicit in almost all cases. The receiving application will already know them. If a destination
|
||||
name is not entirely implicit, information can be included in the application specific data part that
|
||||
will allow the receiver to infer the naming.</p>
|
||||
<p>It is important to note that announces will be forwarded throughout the network according to a
|
||||
certain pattern. This will be detailed in the section
|
||||
<a class="reference internal" href="#understanding-announce"><span class="std std-ref">The Announce Mechanism in Detail</span></a>.</p>
|
||||
<p>In Reticulum, destinations are allowed to move around the network at will. This is very different from
|
||||
protocols such as IP, where an address is always expected to stay within the network segment it was assigned in.
|
||||
This limitation does not exist in Reticulum, and any destination is <em>completely portable</em> over the entire topography
|
||||
of the network, and <em>can even be moved to other Reticulum networks</em> than the one it was created in, and
|
||||
still become reachable. To update it’s reachability, a destination simply needs to send an announce on any
|
||||
networks it is part of. After a short while, it will be globally reachable in the network.</p>
|
||||
<p>Seeing how <em>single</em> destinations are always tied to a private/public key pair leads us to the next topic.</p>
|
||||
</div>
|
||||
<div class="section" id="understanding-identities">
|
||||
<span id="identities"></span><h3>Identities<a class="headerlink" href="#understanding-identities" title="Permalink to this headline">¶</a></h3>
|
||||
<p>In Reticulum, an <em>identity</em> does not necessarily represent a personal identity, but is an abstraction that
|
||||
can represent any kind of <em>verified entity</em>. This could very well be a person, but it could also be the
|
||||
can represent any kind of <em>verifiable entity</em>. This could very well be a person, but it could also be the
|
||||
control interface of a machine, a program, robot, computer, sensor or something else entirely. In
|
||||
general, any kind of agent that can act, or be acted upon, or store or manipulate information, can be
|
||||
represented as an identity.</p>
|
||||
<p>As we have seen, a <em>single</em> destination will always have an <em>identity</em> tied to it, but not <em>plain</em> or <em>group</em>
|
||||
represented as an identity. An <em>identity</em> can be used to create any number of destinations.</p>
|
||||
<p>A <em>single</em> destination will always have an <em>identity</em> tied to it, but not <em>plain</em> or <em>group</em>
|
||||
destinations. Destinations and identities share a multilateral connection. You can create a
|
||||
destination, and if it is not connected to an identity upon creation, it will just create a new one to use
|
||||
automatically. This may be desirable in some situations, but often you will probably want to create
|
||||
the identity first, and then link it to created destinations.</p>
|
||||
<p>Building upon the simple messenger example, we could use an identity to represent the user of the
|
||||
application. Destinations created will then be linked to this identity to allow communication to
|
||||
reach the user. In all cases it is of great importance to store the private keys associated with any
|
||||
Reticulum Identity securely and privately.</p>
|
||||
the identity first, and then use it to create new destinations.</p>
|
||||
<p>As an example, we could use an identity to represent the user of a messaging application.
|
||||
Destinations can then be created by this identity to allow communication to reach the user.
|
||||
In all cases it is of great importance to store the private keys associated with any
|
||||
Reticulum Identity securely and privately, since obtaining access to the identity keys equals
|
||||
obtaining access and controlling reachability to any destinations created by that identity.</p>
|
||||
</div>
|
||||
<div class="section" id="getting-further">
|
||||
<span id="understanding-gettingfurther"></span><h3>Getting Further<a class="headerlink" href="#getting-further" title="Permalink to this headline">¶</a></h3>
|
||||
@@ -330,65 +361,74 @@ hops in the network.</p>
|
||||
</div>
|
||||
<div class="section" id="reticulum-transport">
|
||||
<span id="understanding-transport"></span><h2>Reticulum Transport<a class="headerlink" href="#reticulum-transport" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The term routing has been purposefully avoided until now. The current methods of routing used in IP-based
|
||||
networks are fundamentally incompatible with the physical link types that Reticulum was designed to handle.
|
||||
These routing methodologies assume trust at the physical layer, and often needs a lot more bandwidth than
|
||||
Reticulum can assume is available.</p>
|
||||
<p>Since Reticulum is designed to run over open radio spectrum, no such trust exists, and bandwidth is often
|
||||
very limited. Existing routing protocols like BGP or OSPF carry too much overhead to be practically
|
||||
useable over bandwidth-limited, high-latency links.</p>
|
||||
<p>To overcome such challenges, Reticulum’s <em>Transport</em> system uses public-key cryptography to
|
||||
implement the concept of <em>paths</em> that allow discovery of how to get information to a certain
|
||||
<p>The methods of routing used in traditional networks are fundamentally incompatible with the physical medium
|
||||
types and circumstances that Reticulum was designed to handle. These mechanisms mostly assume trust at the physical layer,
|
||||
and often needs a lot more bandwidth than Reticulum can assume is available. Since Reticulum is designed to
|
||||
survive running over open radio spectrum, no such trust can be assumed, and bandwidth is often very limited.</p>
|
||||
<p>To overcome such challenges, Reticulum’s <em>Transport</em> system uses asymmetric elliptic curve cryptography to
|
||||
implement the concept of <em>paths</em> that allow discovery of how to get information closer to a certain
|
||||
destination. It is important to note that no single node in a Reticulum network knows the complete
|
||||
path to a destination. Every Transport node participating in a Reticulum network will only
|
||||
know what the most direct way to get a packet one hop closer to it’s destination is.</p>
|
||||
know the most direct way to get a packet one hop closer to it’s destination.</p>
|
||||
<div class="section" id="node-types">
|
||||
<span id="understanding-nodetypes"></span><h3>Node Types<a class="headerlink" href="#node-types" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Currently, Reticulum distinguishes between two types of network nodes. All nodes on a Reticulum network
|
||||
are <em>Reticulum Instances</em>, and some are alo <em>Transport Nodes</em>. If a system running Reticulum is fixed in
|
||||
one place, and is intended to be kept available most of the time, it is a good contender to be a <em>Transport Node</em>.</p>
|
||||
<p>Any Reticulum Instance can become a Transport Node by enabling it in the configuration.
|
||||
This distinction is made by the user configuring the node, and is used to determine what nodes on the
|
||||
network will help forward traffic, and what nodes rely on other nodes for wider connectivity.</p>
|
||||
<p>If a node is an <em>Instance</em> it should be given the configuration directive <code class="docutils literal notranslate"><span class="pre">enable_transport</span> <span class="pre">=</span> <span class="pre">No</span></code>, which
|
||||
is the default setting.</p>
|
||||
<p>If it is a <em>Transport Node</em>, it should be given the configuration directive <code class="docutils literal notranslate"><span class="pre">enable_transport</span> <span class="pre">=</span> <span class="pre">Yes</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="the-announce-mechanism-in-detail">
|
||||
<span id="understanding-announce"></span><h3>The Announce Mechanism in Detail<a class="headerlink" href="#the-announce-mechanism-in-detail" title="Permalink to this headline">¶</a></h3>
|
||||
<p>When an <em>announce</em> is transmitted by a node, it will be forwarded by any node receiving it, but
|
||||
according to some specific rules:</p>
|
||||
<p>When an <em>announce</em> for a destination is transmitted by from a Reticulum instance, it will be forwarded by
|
||||
any transport node receiving it, but according to some specific rules:</p>
|
||||
<ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">If this exact announce has already been received before, ignore it.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">If not, record into a table which node the announce was received from, and how many times in
|
||||
<div class="line">If not, record into a table which Transport Node the announce was received from, and how many times in
|
||||
total it has been retransmitted to get here.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">If the announce has been retransmitted <em>m+1</em> times, it will not be forwarded. By default, <em>m</em> is
|
||||
set to 18.</div>
|
||||
<div class="line">If the announce has been retransmitted <em>m+1</em> times, it will not be forwarded any more. By default, <em>m</em> is
|
||||
set to 128.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The announce will be assigned a delay <em>d</em> = c<sup>h</sup> seconds, where <em>c</em> is a decay constant, and <em>h</em> is the amount of times this packet has already been forwarded.</div>
|
||||
<div class="line">After a randomised delay, the announce will be retransmitted on all interfaces that have bandwidth
|
||||
available for processing announces. By default, the maximum bandwidth allocation for processing
|
||||
announces is set at 2%, but can be configured on a per-interface basis.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The packet will be given a priority <em>p = 1/d</em>.</div>
|
||||
<div class="line">If any given interface does not have enough bandwidth available for retransmitting the announce,
|
||||
the announce will be assigned a priority inversely proportional to it’s hop count, and be inserted
|
||||
into a queue managed by the interface.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">If at least <em>d</em> seconds has passed since the announce was received, and no other packets with a
|
||||
priority higher than <em>p</em> are waiting in the queue (see Packet Prioritisation), and the channel is
|
||||
not utilized by other traffic, the announce will be forwarded.</div>
|
||||
<div class="line">When the interface has bandwidth available for processing an announce, it will prioritise announces
|
||||
for destinations that are closest in terms of hops, thus prioritising reachability and connectivity
|
||||
of local nodes, even on slow networks that connect to wider and faster networks.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">If no other nodes are heard retransmitting the announce with a greater hop count than when
|
||||
it left this node, transmitting it will be retried <em>r</em> times. By default, <em>r</em> is set to 1. Retries
|
||||
follow same rules as above, with the exception that it must wait for at least <em>d</em> = c<sup>h+1</sup> +
|
||||
t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
|
||||
node to retransmit the packet, plus a random window. By default, <em>t</em> is set to 10 seconds, and the
|
||||
random window <em>rw</em> is set to 10 seconds.</div>
|
||||
<div class="line">After the announce has been re-transmitted, and if no other nodes are heard retransmitting the announce
|
||||
with a greater hop count than when it left this node, transmitting it will be retried <em>r</em> times. By default,
|
||||
<em>r</em> is set to 1.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">If a newer announce from the same destination arrives, while an identical one is already in
|
||||
the queue, the newest announce is discarded. If the newest announce contains different
|
||||
application specific data, it will replace the old announce, but will use <em>d</em> and <em>p</em> of the old
|
||||
announce.</div>
|
||||
<div class="line">If a newer announce from the same destination arrives, while an identical one is already waiting
|
||||
to be transmitted, the newest announce is discarded. If the newest announce contains different
|
||||
application specific data, it will replace the old announce.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -397,11 +437,15 @@ node will be able to reach the destination the announce originated from, simply
|
||||
addressed to that destination. Any node with knowledge of the announce will be able to direct the
|
||||
packet towards the destination by looking up the next node with the shortest amount of hops to the
|
||||
destination.</p>
|
||||
<p>According to these rules and default constants, an announce will propagate throughout the network
|
||||
in a predictable way. In an example network utilising the default constants, and with an average link
|
||||
distance of <em>Lavg =</em> 15 kilometers, an announce will be able to propagate outwards to a radius of 180
|
||||
kilometers in 34 minutes, and a <em>maximum announce radius</em> of 270 kilometers in approximately 3
|
||||
days.</p>
|
||||
<p>According to these rules, an announce will propagate throughout the network in a predictable way,
|
||||
and make the announced destination reachable in a short amount of time. Fast networks that have the
|
||||
capacity to process many announces can reach full convergence very quickly, even when constantly adding
|
||||
new destinations. Slower segments of such networks might take a bit longer to gain full knowledge about
|
||||
the wide and fast networks they are connected to, but can still do so over time, while prioritising full
|
||||
and quickly converging end-to-end connectivity for their local, slower segments.</p>
|
||||
<p>In general, even extremely complex networks, that utilize the maximum 128 hops will converge to full
|
||||
end-to-end connectivity in about one minute, given there is enough bandwidth available to process
|
||||
the required amount of announces.</p>
|
||||
</div>
|
||||
<div class="section" id="reaching-the-destination">
|
||||
<span id="understanding-paths"></span><h3>Reaching the Destination<a class="headerlink" href="#reaching-the-destination" title="Permalink to this headline">¶</a></h3>
|
||||
@@ -459,7 +503,7 @@ strictly necessary to use one of the others.</div>
|
||||
<ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">First, the node that wishes to establish a link will send out a special packet, that
|
||||
traverses the network and locates the desired destination. Along the way, the nodes that
|
||||
traverses the network and locates the desired destination. Along the way, the Transport Nodes that
|
||||
forward the packet will take note of this <em>link request</em>.</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -476,10 +520,10 @@ remember the <em>link</em> , and it can subsequently be used by referring to a h
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">As a part of the <em>link request</em> , a Diffie-Hellman key exchange takes place, that sets up an
|
||||
efficiently encrypted tunnel between the two nodes, using elliptic curve cryptography. As such,
|
||||
this mode of communication is preferred, even for situations when nodes can directly communicate,
|
||||
when the amount of data to be exchanged numbers in the tens of packets.</div>
|
||||
<div class="line">As a part of the <em>link request</em>, an Elliptic Curve Diffie-Hellman key exchange takes place, that sets up an
|
||||
efficiently encrypted tunnel between the two nodes. As such, this mode of communication is preferred,
|
||||
even for situations when nodes can directly communicate, when the amount of data to be exchanged numbers
|
||||
in the tens of packets, or whenever the use of the more advanced API functions is desired.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
@@ -488,6 +532,12 @@ the same <em>proof</em> mechanism discussed before, so the sending node can obta
|
||||
that the information reached the intended recipient.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">Once the <em>link</em> has been set up, the initiator can remain anonymous, or choose to authenticate towards
|
||||
the destination using a Reticulum Identity. This authentication is happening inside the encrypted
|
||||
link, and is only revealed to the verified destination, and no intermediaries.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p>In a moment, we will discuss the details of how this methodology is implemented, but let’s first
|
||||
recap what purposes this methodology serves. We first ensure that the node answering our request
|
||||
@@ -587,93 +637,102 @@ of codes to reliably transfer any amount of data. They can be used to transfer d
|
||||
or stream data directly from files.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="reference-system-setup">
|
||||
<span id="understanding-referencesystem"></span><h2>Reference System Setup<a class="headerlink" href="#reference-system-setup" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This section will detail the recommended <em>Reference System Setup</em> for Reticulum. It is important to
|
||||
note that Reticulum is designed to be usable over more or less any medium that allows you to send
|
||||
and receive data in a digital form, and satisfies some very low minimum requirements. The
|
||||
communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 1000 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
Reticulum software should be able to run on more or less any hardware that can provide a Python 3.x
|
||||
<div class="section" id="reference-setup">
|
||||
<span id="understanding-referencesystem"></span><h2>Reference Setup<a class="headerlink" href="#reference-setup" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This section will detail a recommended <em>Reference Setup</em> for Reticulum. It is important to
|
||||
note that Reticulum is designed to be usable on more or less any computing device, and over more
|
||||
or less any medium that allows you to send and receive data, which satisfies some very low
|
||||
minimum requirements.</p>
|
||||
<p>The communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
||||
runtime environment.</p>
|
||||
<p>That being said, the reference setup has been outlined to provide a common platform for anyone
|
||||
<p>That being said, this reference setup has been outlined to provide a common platform for anyone
|
||||
who wants to help in the development of Reticulum, and for everyone who wants to know a
|
||||
recommended setup to get started. A reference system consists of three parts:</p>
|
||||
recommended setup to get started experimenting. A reference system consists of three parts:</p>
|
||||
<ul class="simple">
|
||||
<li><dl class="simple">
|
||||
<dt><strong>A channel access device</strong></dt><dd><p>Or <em>CAD</em> , in short, provides access to the physical medium whereupon the communication
|
||||
<dt><strong>An Interface Device</strong></dt><dd><p>Which provides access to the physical medium whereupon the communication
|
||||
takes place, for example a radio with an integrated modem. A setup with a separate modem
|
||||
connected to a radio would also be termed a “channel access device”.</p>
|
||||
connected to a radio would also be an interface device.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>A host device</strong></dt><dd><p>Some sort of computing device that can run the necessary software, communicates with the
|
||||
channel access device, and provides user interaction.</p>
|
||||
<dt><strong>A Host Device</strong></dt><dd><p>Some sort of computing device that can run the necessary software, communicate with the
|
||||
interface device, and provide user interaction.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>A software stack</strong></dt><dd><p>The software implementing the Reticulum protocol and applications using it.</p>
|
||||
<dt><strong>A Software Stack</strong></dt><dd><p>The software implementing the Reticulum protocol and applications using it.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
<p>The reference setup can be considered a relatively stable platform to develop on, and also to start
|
||||
building networks on. While details of the implementation might change at the current stage of
|
||||
building networks or applications on. While details of the implementation might change at the current stage of
|
||||
development, it is the goal to maintain hardware compatibility for as long as entirely possible, and
|
||||
the current reference setup has been determined to provide a functional platform for many years
|
||||
into the future. The current Reference System Setup is as follows:</p>
|
||||
<ul class="simple">
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Channel Access Device</strong></dt><dd><p>A data radio consisting of a LoRa radio module, and a microcontroller with open source
|
||||
<dt><strong>Interface Device</strong></dt><dd><p>A data radio consisting of a LoRa radio module, and a microcontroller with open source
|
||||
firmware, that can connect to host devices via USB. It operates in either the 430, 868 or 900
|
||||
MHz frequency bands. More details can be found on the <a class="reference external" href="https://unsigned.io/rnode">RNode Page</a>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Host device</strong></dt><dd><p>Any computer device running Linux and Python. A Raspberry Pi with a Debian based OS is
|
||||
<dt><strong>Host Device</strong></dt><dd><p>Any computer device running Linux and Python. A Raspberry Pi with a Debian based OS is
|
||||
recommended.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Software stack</strong></dt><dd><p>The current Reference Implementation Release of Reticulum, running on a Debian based
|
||||
<dt><strong>Software Stack</strong></dt><dd><p>The most recently released Python Implementation of Reticulum, running on a Debian based
|
||||
operating system.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
<p>It is very important to note, that the reference channel access device <strong>does not</strong> use the LoRaWAN
|
||||
standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will
|
||||
need a plain LoRa radio module connected to an MCU with the correct firmware. Full details on how to
|
||||
<p>To avoid confusion, it is very important to note, that the reference interface device <strong>does not</strong>
|
||||
use the LoRaWAN standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will
|
||||
need a plain LoRa radio module connected to an controller with the correct firmware. Full details on how to
|
||||
get or make such a device is available on the <a class="reference external" href="https://unsigned.io/rnode">RNode Page</a>.</p>
|
||||
<p>With the current reference setup, it should be possible to get on a Reticulum network for around 100$
|
||||
even if you have none of the hardware already, and need to purchase everything.</p>
|
||||
<p>This reference setup is of course just a recommendation for getting started easily, and you should
|
||||
tailor it to your own specific needs, or whatever hardware you have available.</p>
|
||||
</div>
|
||||
<div class="section" id="protocol-specifics">
|
||||
<span id="understanding-protocolspecifics"></span><h2>Protocol Specifics<a class="headerlink" href="#protocol-specifics" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This chapter will detail protocol specific information that is essential to the implementation of
|
||||
Reticulum, but non critical in understanding how the protocol works on a general level. It should be
|
||||
treated more as a reference than as essential reading.</p>
|
||||
<div class="section" id="node-types">
|
||||
<h3>Node Types<a class="headerlink" href="#node-types" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Currently Reticulum defines two node types, the <em>Station</em> and the <em>Peer</em>. A node is a <em>station</em> if it fixed
|
||||
in one place, and if it is intended to be kept online most of the time. Otherwise the node is a <em>peer</em>.</p>
|
||||
<p>This distinction is made by the user configuring the node, and is used to determine what nodes on the
|
||||
network will help forward traffic, and what nodes rely on other nodes for connectivity.</p>
|
||||
<p>If a node is a <em>Peer</em> it should be given the configuration directive <code class="docutils literal notranslate"><span class="pre">enable_transport</span> <span class="pre">=</span> <span class="pre">No</span></code>.</p>
|
||||
<p>If it is a <em>Station</em>, it should be given the configuration directive <code class="docutils literal notranslate"><span class="pre">enable_transport</span> <span class="pre">=</span> <span class="pre">Yes</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="packet-prioritisation">
|
||||
<h3>Packet Prioritisation<a class="headerlink" href="#packet-prioritisation" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Currently, Reticulum is completely priority-agnostic regarding general traffic. All traffic is handled
|
||||
on a first-come, first-serve basis. Announce re-transmission are handled according to the re-transmission
|
||||
times and priorities described earlier in this chapter.</p>
|
||||
</div>
|
||||
<div class="section" id="binary-packet-format">
|
||||
<span id="understanding-packetformat"></span><h3>Binary Packet Format<a class="headerlink" href="#binary-packet-format" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="section" id="interface-access-codes">
|
||||
<h3>Interface Access Codes<a class="headerlink" href="#interface-access-codes" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Reticulum can create named virtual networks, and networks that are only accessible by knowing a preshared
|
||||
passphrase. The configuration of this is detailed in the <a class="reference internal" href="interfaces.html#interfaces-options"><span class="std std-ref">Common Interface Options</span></a>
|
||||
section. To implement these feature, Reticulum uses the concept of Interface Access Codes, that are calculated
|
||||
and verified per packet.</p>
|
||||
<p>An interface with a named virtual network or passphrase authentication enabled will derive a shared Ed25519
|
||||
signing identity, and for every outbound packet generate a signature of the entire packet. This signature is
|
||||
then inserted into the packet as an Interface Access Code before transmission. Depending on the speed and
|
||||
capabilities of the interface, the IFAC can be the full 512-bit Ed25519 signature, or a truncated version.
|
||||
Configured IFAC length can be inspected for all interfaces with the <code class="docutils literal notranslate"><span class="pre">rnstatus</span></code> utility.</p>
|
||||
<p>Upon receipt, the interface will check that the signature matches the expected value, and drop the packet if it
|
||||
does not. This ensures that only packets sent with the correct naming and/or passphrase parameters are allowed to
|
||||
pass onto the network.</p>
|
||||
</div>
|
||||
<div class="section" id="wire-format">
|
||||
<span id="understanding-packetformat"></span><h3>Wire Format<a class="headerlink" href="#wire-format" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>== Reticulum Wire Format ======
|
||||
|
||||
A Reticulum packet is composed of the following fields:
|
||||
@@ -681,9 +740,14 @@ A Reticulum packet is composed of the following fields:
|
||||
[HEADER 2 bytes] [ADDRESSES 10/20 bytes] [CONTEXT 1 byte] [DATA 0-477 bytes]
|
||||
|
||||
* The HEADER field is 2 bytes long.
|
||||
* Byte 1: [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 2: Number of hops
|
||||
|
||||
* Interface Access Code field if the IFAC flag was set.
|
||||
* The length of the Interface Access Code can vary from
|
||||
1 to 64 bytes according to physical interface
|
||||
capabilities and configuration.
|
||||
|
||||
* The ADDRESSES field contains either 1 or 2 addresses.
|
||||
* Each address is 10 bytes long.
|
||||
* The Header Type flag in the HEADER field determines
|
||||
@@ -696,12 +760,16 @@ A Reticulum packet is composed of the following fields:
|
||||
* The DATA field is between 0 and 477 bytes.
|
||||
* It contains the packets data payload.
|
||||
|
||||
IFAC Flag
|
||||
-----------------
|
||||
open 0 Packet for publically accessible interface
|
||||
authenticated 1 Interface authentication is included in packet
|
||||
|
||||
|
||||
Header Types
|
||||
-----------------
|
||||
type 1 00 Two byte header, one 10 byte address field
|
||||
type 2 01 Two byte header, two 10 byte address fields
|
||||
type 3 10 Reserved
|
||||
type 4 11 Reserved
|
||||
type 1 0 Two byte header, one 10 byte address field
|
||||
type 2 1 Two byte header, two 10 byte address fields
|
||||
|
||||
|
||||
Propagation Types
|
||||
@@ -730,45 +798,63 @@ proof 11
|
||||
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD
|
||||
HEADER FIELD DESTINATION FIELDS CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ________________|________________ ________|______ __|_
|
||||
| | | | | | | |
|
||||
01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
| | | | |
|
||||
| | | | +-- Hops = 4
|
||||
| | | +------- Packet Type = DATA
|
||||
| | +--------- Destination Type = SINGLE
|
||||
| +----------- Propagation Type = TRANSPORT
|
||||
+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
01010000 00000100 [HASH1, 10 bytes] [HASH2, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 4
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = TRANSPORT
|
||||
|+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
+- Packet Example -+
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD
|
||||
HEADER FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ _______|_______ ________|______ __|_
|
||||
| | | | | | | |
|
||||
00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
| | | | |
|
||||
| | | | +-- Hops = 7
|
||||
| | | +------- Packet Type = DATA
|
||||
| | +--------- Destination Type = SINGLE
|
||||
| +----------- Propagation Type = BROADCAST
|
||||
+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
00000000 00000111 [HASH1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
+- Packet Example -+
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size including all fields.
|
||||
HEADER FIELD IFAC FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ______|______ _______|_______ ________|______ __|_
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = ENABLED
|
||||
|
||||
- Path Request : 33 bytes
|
||||
- Announce : 151 bytes
|
||||
- Link Request : 77 bytes
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size counting all fields including headers,
|
||||
but excluding any interface access codes.
|
||||
|
||||
- Path Request : 33 bytes
|
||||
- Announce : 151 bytes
|
||||
- Link Request : 77 bytes
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -798,6 +884,7 @@ proof 11
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#reticulum-transport">Reticulum Transport</a><ul>
|
||||
<li><a class="reference internal" href="#node-types">Node Types</a></li>
|
||||
<li><a class="reference internal" href="#the-announce-mechanism-in-detail">The Announce Mechanism in Detail</a></li>
|
||||
<li><a class="reference internal" href="#reaching-the-destination">Reaching the Destination</a><ul>
|
||||
<li><a class="reference internal" href="#link-establishment-in-detail">Link Establishment in Detail</a></li>
|
||||
@@ -806,11 +893,11 @@ proof 11
|
||||
<li><a class="reference internal" href="#resources">Resources</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#reference-system-setup">Reference System Setup</a></li>
|
||||
<li><a class="reference internal" href="#reference-setup">Reference Setup</a></li>
|
||||
<li><a class="reference internal" href="#protocol-specifics">Protocol Specifics</a><ul>
|
||||
<li><a class="reference internal" href="#node-types">Node Types</a></li>
|
||||
<li><a class="reference internal" href="#packet-prioritisation">Packet Prioritisation</a></li>
|
||||
<li><a class="reference internal" href="#binary-packet-format">Binary Packet Format</a></li>
|
||||
<li><a class="reference internal" href="#interface-access-codes">Interface Access Codes</a></li>
|
||||
<li><a class="reference internal" href="#wire-format">Wire Format</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -856,7 +943,7 @@ proof 11
|
||||
<li class="right" >
|
||||
<a href="interfaces.html" title="Supported Interfaces"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+49
-27
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Using Reticulum on Your System — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>Using Reticulum on Your System — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="gettingstartedfast.html" title="Getting Started Fast"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Using Reticulum on Your System</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -45,15 +45,17 @@
|
||||
<span id="using-main"></span><h1>Using Reticulum on Your System<a class="headerlink" href="#using-reticulum-on-your-system" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Reticulum is not installed as a driver or kernel module, as one might expect
|
||||
of a networking stack. Instead, Reticulum is distributed as a Python module.
|
||||
This means that no special privileges are required to install or use it.
|
||||
This means that no special privileges are required to install or use it. It
|
||||
is also very light-weight, and easy to transfer to and install on new systems.
|
||||
Any program or application that uses Reticulum will automatically load and
|
||||
initialise Reticulum when it starts.</p>
|
||||
<p>In many cases, this approach is sufficient. When any program needs to use
|
||||
Reticulum, it is loaded, initialised, interfaces are brought up, and the
|
||||
program can now communicate over Reticulum. If another program starts up
|
||||
and also wants access to the same Reticulum network, the instance is simply
|
||||
shared. This works for any number of programs running concurrently, and is
|
||||
very easy to use, but depending on your use case, there are other options.</p>
|
||||
program can now communicate over any Reticulum networks available. If another
|
||||
program starts up and also wants access to the same Reticulum network, the
|
||||
instance is simply shared. This works for any number of programs running
|
||||
concurrently, and is very easy to use, but depending on your use case, there
|
||||
are other options.</p>
|
||||
<div class="section" id="included-utility-programs">
|
||||
<h2>Included Utility Programs<a class="headerlink" href="#included-utility-programs" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If you often use Reticulum from several different programs, or simply want
|
||||
@@ -98,32 +100,48 @@ rnstatus
|
||||
|
||||
# Example output
|
||||
Shared Instance[37428]
|
||||
Status: Up
|
||||
Connected applications: 1
|
||||
RX: 1.13 KB
|
||||
TX: 1.07 KB
|
||||
Status : Up
|
||||
Serving : 1 program
|
||||
Rate : 1.00 Gbps
|
||||
Traffic : 83.13 KB↑
|
||||
86.10 KB↓
|
||||
|
||||
UDPInterface[Default UDP Interface/0.0.0.0:4242]
|
||||
Status: Up
|
||||
RX: 1.01 KB
|
||||
TX: 1.01 KB
|
||||
AutoInterface[Local]
|
||||
Status : Up
|
||||
Mode : Full
|
||||
Rate : 10.00 Mbps
|
||||
Peers : 1 reachable
|
||||
Traffic : 63.23 KB↑
|
||||
80.17 KB↓
|
||||
|
||||
TCPInterface[RNS Testnet Frankfurt/frankfurt.rns.unsigned.io:4965]
|
||||
Status: Up
|
||||
RX: 1.37 KB
|
||||
TX: 9.02 KB
|
||||
Status : Up
|
||||
Mode : Full
|
||||
Rate : 10.00 Mbps
|
||||
Traffic : 187.27 KB↑
|
||||
74.17 KB↓
|
||||
|
||||
RNodeInterface[RNode UHF]
|
||||
Status : Up
|
||||
Mode : Access Point
|
||||
Rate : 1.30 kbps
|
||||
Access : 64-bit IFAC by <…e702c42ba8>
|
||||
Traffic : 8.49 KB↑
|
||||
9.23 KB↓
|
||||
|
||||
Reticulum Transport Instance <5245a8efe1788c6a70e1> running
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnsd [-h] [--config CONFIG] [-v] [-q] [--version]
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus [-h] [--config CONFIG] [--version] [-a] [-v]
|
||||
|
||||
Reticulum Network Stack Daemon
|
||||
Reticulum Network Stack Status
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
-v, --verbose
|
||||
-q, --quiet
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-v, --verbose
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,7 +156,8 @@ rnpath eca6f4e4dc26ae329e61
|
||||
Path found, destination <eca6f4e4dc26ae329e61> is 4 hops away via <56b115c30cd386cad69c> on TCPInterface[Testnet/frankfurt.rns.unsigned.io:4965]
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath.py [-h] [--config CONFIG] [--version] [-v] [destination]
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-d] [-w seconds] [-v]
|
||||
[destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
@@ -149,6 +168,9 @@ optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-d, --drop remove the path to a destination
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
</pre></div>
|
||||
</div>
|
||||
@@ -192,8 +214,8 @@ few system configuration changes that can make this easier to administrate.
|
||||
These changes will be detailed here.</p>
|
||||
<div class="section" id="fixed-serial-port-names">
|
||||
<h3>Fixed Serial Port Names<a class="headerlink" href="#fixed-serial-port-names" title="Permalink to this headline">¶</a></h3>
|
||||
<p>On a Reticulum node with several serial port based interfaces, it can be
|
||||
beneficial to use the fixed name device nodes for the serial ports, instead
|
||||
<p>On a Reticulum instance with several serial port based interfaces, it can be
|
||||
beneficial to use the fixed device names for the serial ports, instead
|
||||
of the dynamically allocated shorthands such as <code class="docutils literal notranslate"><span class="pre">/dev/ttyUSB0</span></code>. Under most
|
||||
Debian-based distributions, including Ubuntu and Raspberry Pi OS, these nodes
|
||||
can be found under <code class="docutils literal notranslate"><span class="pre">/dev/serial/by-id</span></code>.</p>
|
||||
@@ -215,7 +237,7 @@ Here is an example of a packet radio TNC configured as such:</p>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Using this methodology avoids potential naming mix-ups where physical devices
|
||||
might be plugged and unplugged in different orders, or when node name
|
||||
might be plugged and unplugged in different orders, or when device name
|
||||
assignment varies from one boot to another.</p>
|
||||
</div>
|
||||
<div class="section" id="reticulum-as-a-system-service">
|
||||
@@ -330,7 +352,7 @@ WantedBy=multi-user.target
|
||||
<li class="right" >
|
||||
<a href="gettingstartedfast.html" title="Getting Started Fast"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Using Reticulum on Your System</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+42
-22
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>What is Reticulum? — Reticulum Network Stack 0.3.0 beta documentation</title>
|
||||
<title>What is Reticulum? — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Reticulum Network Stack Manual"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -43,13 +43,13 @@
|
||||
|
||||
<div class="section" id="what-is-reticulum">
|
||||
<h1>What is Reticulum?<a class="headerlink" href="#what-is-reticulum" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, that can operate even with very high latency and extremely low bandwidth.</p>
|
||||
<p>Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p>
|
||||
<p>Reticulum is a cryptography-based networking stack for building wide-area networks with readily available hardware, that can continue to operate even with extremely low bandwidth and very high latency.</p>
|
||||
<p>Reticulum allows you to build wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p>
|
||||
<p>Reticulum is a complete networking stack, and does not need IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.</p>
|
||||
<p>No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3. Reticulum runs well even on small single-board computers like the Pi Zero.</p>
|
||||
<div class="section" id="current-status">
|
||||
<h2>Current Status<a class="headerlink" href="#current-status" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Reticulum should currently be considered beta software. All core protocol 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.</p>
|
||||
<p>Reticulum should currently be considered beta software. All core protocol 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 stable at the moment, but could change if absolutely warranted.</p>
|
||||
</div>
|
||||
<div class="section" id="what-does-reticulum-offer">
|
||||
<h2>What does Reticulum Offer?<a class="headerlink" href="#what-does-reticulum-offer" title="Permalink to this headline">¶</a></h2>
|
||||
@@ -57,8 +57,8 @@
|
||||
<li><p>Coordination-less globally unique adressing and identification</p></li>
|
||||
<li><p>Fully self-configuring multi-hop routing</p></li>
|
||||
<li><p>Complete initiator anonymity, communicate without revealing your identity</p></li>
|
||||
<li><p>Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication</p></li>
|
||||
<li><p>Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519</p></li>
|
||||
<li><p>Asymmetric encryption based on X25519, and Ed25519 signatures as a basis for all communication</p></li>
|
||||
<li><p>Forward Secrecy by using ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519</p></li>
|
||||
<li><p>Reticulum uses the <a class="reference external" href="https://github.com/fernet/spec/blob/master/Spec.md">Fernet</a> specification for on-the-wire / over-the-air encryption</p>
|
||||
<ul>
|
||||
<li><p>All keys are ephemeral and derived from an ECDH key exchange on Curve25519</p></li>
|
||||
@@ -70,6 +70,12 @@
|
||||
<li><p>Unforgeable packet delivery confirmations</p></li>
|
||||
<li><p>A variety of supported interface types</p></li>
|
||||
<li><p>An intuitive and developer-friendly API</p></li>
|
||||
<li><p>Efficient link establishment</p>
|
||||
<ul>
|
||||
<li><p>Total bandwidth cost of setting up a link is only 3 packets, totalling 237 bytes</p></li>
|
||||
<li><p>Low cost of keeping links open at only 0.62 bits per second</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>Reliable and efficient transfer of arbritrary amounts of data</p>
|
||||
<ul>
|
||||
<li><p>Reticulum can handle a few bytes of data or files of many gigabytes</p></li>
|
||||
@@ -77,12 +83,8 @@
|
||||
<li><p>The API is very easy to use, and provides transfer progress</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>Efficient link establishment</p>
|
||||
<ul>
|
||||
<li><p>Total bandwidth cost of setting up a link is only 3 packets, totalling 237 bytes</p></li>
|
||||
<li><p>Low cost of keeping links open at only 0.62 bits per second</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>Authentication and virtual network segmentation on all supported interface types</p></li>
|
||||
<li><p>Flexible scalability allowing extremely low-bandwidth networks to co-exist and interoperate with large, high-bandwidth networks</p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="where-can-reticulum-be-used">
|
||||
@@ -93,9 +95,9 @@ modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||
of the types of interfaces Reticulum was designed for.</p>
|
||||
<p>An open-source LoRa-based interface called <a class="reference external" href="https://unsigned.io/rnode">RNode</a>
|
||||
has been designed specifically for use with Reticulum. It is possible to build
|
||||
yourself, or it can be purchased as a complete transceiver that just needs a
|
||||
USB connection to the host.</p>
|
||||
has been designed as an example transceiver that is very suitable for
|
||||
Reticulum. It is possible to build it yourself, to transform a common LoRa
|
||||
development board into one, or it can be purchased as a complete transceiver.</p>
|
||||
<p>Reticulum can also be encapsulated over existing IP networks, so there’s
|
||||
nothing stopping you from using it over wired ethernet or your local WiFi
|
||||
network, where it’ll work just as well. In fact, one of the strengths of
|
||||
@@ -103,18 +105,36 @@ Reticulum is how easily it allows you to connect different mediums into a
|
||||
self-configuring, resilient and encrypted mesh.</p>
|
||||
<p>As an example, it’s possible to set up a Raspberry Pi connected to both a
|
||||
LoRa radio, a packet radio TNC and a WiFi network. Once the interfaces are
|
||||
configured, Reticulum will take care of the rest, and any device on the WiFi
|
||||
added, Reticulum will take care of the rest, and any device on the WiFi
|
||||
network can communicate with nodes on the LoRa and packet radio sides of the
|
||||
network, and vice versa.</p>
|
||||
</div>
|
||||
<div class="section" id="interface-types-and-devices">
|
||||
<h2>Interface Types and Devices<a class="headerlink" href="#interface-types-and-devices" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Reticulum implements a range of generalised interface types that covers most of the communications hardware that Reticulum can run over. If your hardware is not supported, it’s relatively simple to implement an interface class. Currently, the following interfaces are supported:</p>
|
||||
<p>Reticulum implements a range of generalised interface types that covers the communications hardware that Reticulum can run over. If your hardware is not supported, it’s relatively simple to implement an interface class. Currently, Reticulum can use the following devices and communication mediums:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Any ethernet device</p></li>
|
||||
<li><p>LoRa using <a class="reference external" href="https://unsigned.io/rnode">RNode</a></p></li>
|
||||
<li><p>Packet Radio TNCs, such as <a class="reference external" href="https://unsigned.io/openmodem">OpenModem</a></p></li>
|
||||
<li><p>Any ethernet device</p>
|
||||
<ul>
|
||||
<li><p>WiFi devices</p></li>
|
||||
<li><p>Wired ethernet devices</p></li>
|
||||
<li><p>Fibre-optic transceivers</p></li>
|
||||
<li><p>Data radios with ethernet ports</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>LoRa using <a class="reference external" href="https://unsigned.io/rnode">RNode</a></p>
|
||||
<ul>
|
||||
<li><p>Can be installed on <a class="reference external" href="https://github.com/markqvist/rnodeconfigutil#supported-devices">many popular LoRa boards</a></p></li>
|
||||
<li><p>Can be purchased as a <a class="reference external" href="https://unsigned.io/rnode">ready to use transceiver</a></p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>Packet Radio TNCs, such as <a class="reference external" href="https://unsigned.io/openmodem">OpenModem</a></p>
|
||||
<ul>
|
||||
<li><p>Any packet radio TNC in KISS mode</p></li>
|
||||
<li><p>Ideal for VHF and UHF radio</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>Any device with a serial port</p></li>
|
||||
<li><p>The I2P network</p></li>
|
||||
<li><p>TCP over IP networks</p></li>
|
||||
<li><p>UDP over IP networks</p></li>
|
||||
</ul>
|
||||
@@ -184,7 +204,7 @@ network, and vice versa.</p>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Reticulum Network Stack Manual"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.0 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ copyright = '2021, Mark Qvist'
|
||||
author = 'Mark Qvist'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.3.0 beta'
|
||||
release = '0.3.5 beta'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@@ -65,4 +65,4 @@ html_static_path = ['_static']
|
||||
# return False
|
||||
|
||||
# def setup(app):
|
||||
# app.connect('autodoc-skip-member', check_skip_member)
|
||||
# app.connect('autodoc-skip-member', check_skip_member)
|
||||
|
||||
@@ -6,11 +6,31 @@ The best way to get started with the Reticulum Network Stack depends on what
|
||||
you want to do. This guide will outline sensible starting paths for different
|
||||
scenarios.
|
||||
|
||||
|
||||
Try Using a Reticulum-based Program
|
||||
=============================================
|
||||
If you simply want to try using a program built with Reticulum, you can take
|
||||
a look at `Nomad Network <https://github.com/markqvist/nomadnet>`_, which
|
||||
provides a complete encrypted communications suite built with Reticulum.
|
||||
|
||||
If you simply want to try using a program built with Reticulum, a few different
|
||||
programs exist that allow basic communication and a range of other useful functions
|
||||
over even extremely low-bandwidth Reticulum networks.
|
||||
|
||||
These programs will let you get a feel for how Reticulum works. They have been designed
|
||||
to run well over networks based on LoRa or packet radio, but can also be used completely
|
||||
over local WiFi, wired ethernet, the Internet, or any combination.
|
||||
|
||||
As such, it is easy to get started experimenting, without having to set up any radio
|
||||
transceivers or infrastructure just to try it out. Launching the programs on separate
|
||||
devices connected to the same WiFi network is enough to get started, and physical
|
||||
radio interfaces can then be added later.
|
||||
|
||||
Nomad Network
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The terminal-based program `Nomad Network <https://github.com/markqvist/nomadnet>`_
|
||||
provides a complete encrypted communications suite built with Reticulum. It features
|
||||
encrypted messaging (both direct and delayed-delivery for offline users), file sharing,
|
||||
and has a built-in text-browser and page server with support for dynamically rendered pages,
|
||||
user authentication and more.
|
||||
|
||||
.. image:: screenshots/nomadnet_3.png
|
||||
:target: _images/nomadnet_3.png
|
||||
@@ -29,7 +49,25 @@ You can install Nomad Network via pip:
|
||||
# ... and run
|
||||
nomadnet
|
||||
|
||||
**Please Note**: If this is the very first time you use pip to install a program
|
||||
on your system, you might need to reboot your system for your program to become
|
||||
available. If you get a "command not found" error or similar when running the
|
||||
program, reboot your system and try again.
|
||||
|
||||
Sideband
|
||||
^^^^^^^^
|
||||
|
||||
If you would rather use a program with a graphical user interface, you can take
|
||||
a look at `Sideband <https://unsigned.io/sideband>`_, which is available for Android,
|
||||
Linux and macOS.
|
||||
|
||||
.. image:: screenshots/sideband_1.png
|
||||
:width: 400px
|
||||
:align: center
|
||||
:target: _images/sideband_1.png
|
||||
|
||||
Sideband is currently in the early stages of development, but already provides basic
|
||||
communication features, and interoperates with Nomad Network, or any other LXMF client.
|
||||
|
||||
Using the Included Utilities
|
||||
=============================================
|
||||
@@ -44,6 +82,7 @@ network status and connectivity.
|
||||
To learn more about these utility programs, have a look at the
|
||||
:ref:`Using Reticulum on Your System<using-main>` chapter of this manual.
|
||||
|
||||
|
||||
Creating a Network With Reticulum
|
||||
=============================================
|
||||
To create a network, you will need to specify one or more *interfaces* for
|
||||
@@ -53,17 +92,93 @@ or use the interactive ``rnsconfig`` utility.
|
||||
|
||||
When Reticulum is started for the first time, it will create a default
|
||||
configuration file, with one active interface. This default interface uses
|
||||
your existing ethernet network (if there is one), and only allows you to
|
||||
communicate with other Reticulum peers within your local broadcast domain.
|
||||
your existing ethernet and WiFi networks (if any), and only allows you to
|
||||
communicate with other Reticulum peers within your local broadcast domains.
|
||||
|
||||
To communicate further, you will have to add one or more interfaces. The default
|
||||
configuration includes a number of examples, ranging from using TCP over the
|
||||
internet, to LoRa and Packet Radio interfaces.
|
||||
|
||||
With Reticulum, you only need to configure what interfaces you want to communicate
|
||||
over. There is no need to configure address spaces, subnets, routing tables,
|
||||
or other things you might be used to from other network types.
|
||||
|
||||
Once Reticulums knows which interfaces it should use, it will automatically
|
||||
discover topography and configure transport of data to any destinations it
|
||||
knows about.
|
||||
|
||||
In situations where you already have an established WiFi or ethernet network, and
|
||||
many devices that want to utilise the same external Reticulum network (for example over
|
||||
LoRa), it will often be sufficient to let one system act as a Reticulum gateway, by
|
||||
adding any external interfaces to this systems configuration, and enabling transport. Any
|
||||
other device on your local WiFi will then be able to connect to this wider Reticulum
|
||||
network just using the default interface configuration.
|
||||
|
||||
Possibly, the examples in the config file are enough to get you started. If
|
||||
you want more information, you can read the :ref:`Building Networks<networks-main>`
|
||||
and :ref:`Interfaces<interfaces-main>` chapters of this manual.
|
||||
|
||||
Connecting Reticulum Instances Over the Internet
|
||||
================================================
|
||||
Reticulum currently offers two interfaces suitable for connecting instances over the Internet: :ref:`TCP<interfaces-tcps>`
|
||||
and :ref:`I2P<interfaces-i2p>`. Each interface offers a different set of features, and Reticulum
|
||||
users should carefully choose the interface which best suites their needs.
|
||||
|
||||
The ``TCPServerInterface`` allows users to host an instance accessible over TCP/IP. This
|
||||
method is generally faster, lower latency, and more energy efficient than using ``I2PInterface``,
|
||||
however it also leaks more data about the server host.
|
||||
|
||||
TCP connections reveal the IP address of both your instance and the server to anyone who can
|
||||
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
|
||||
inspecting your packets may be able to record packet metadata like time of transmission and packet size.
|
||||
Even though Reticulum encrypts traffic, TCP does not, so an adversary may be able to use
|
||||
packet inspection to learn that a system is running Reticulum, and what other IP adresses connect to it.
|
||||
Hosting a publicly reachable instance over TCP also requires a publicly reachable IP address,
|
||||
which most Internet connections don't offer anymore.
|
||||
|
||||
The ``I2PInterface`` routes messages through the `Invisible Internet Protocol
|
||||
(I2P) <https://geti2p.net/en/>`_. To properly use this interface, users must also run an I2P daemon in
|
||||
parallel to ``rnsd``. For always-on I2P nodes it is recommended to use `i2pd <https://i2pd.website/>`_.
|
||||
|
||||
By default, I2P will encrypt and mix all traffic sent over the Internet, and
|
||||
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
|
||||
will also relay other I2P user's encrypted packets, which will use extra
|
||||
bandwidth and compute power, but also makes timing attacks and other forms of
|
||||
deep-packet-inspection much more difficult.
|
||||
|
||||
I2P also allows users to host globally available Reticulum instances from non-public IPs and behind firewalls.
|
||||
|
||||
In general it is recommended to use an I2P node if you want to host a publically accessible
|
||||
instance, while preserving anonymity. If you care more about performance, and a slightly
|
||||
easier setup, use TCP.
|
||||
|
||||
Connect to the Public Testnet
|
||||
===========================================
|
||||
|
||||
An experimental public testnet has been made accessible over both I2P and TCP. You can join it
|
||||
by adding one of the following interfaces to your ``.reticulum/config`` file:
|
||||
|
||||
.. code::
|
||||
|
||||
# For connecting over TCP/IP:
|
||||
[[RNS Testnet Frankfurt]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = yes
|
||||
outgoing = True
|
||||
target_host = frankfurt.rns.unsigned.io
|
||||
target_port = 4965
|
||||
|
||||
|
||||
# For connecting over I2P:
|
||||
[[RNS Testnet I2P Node A]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
peers = ykzlw5ujbaqc2xkec4cpvgyxj257wcrmmgkuxqmqcur7cq3w3lha.b32.i2p
|
||||
|
||||
Many other Reticulum instances are connecting to this testnet, and you can also join it
|
||||
via other entry points if you know them. There is absolutely no control over the network
|
||||
topography, usage or what types of instances connect. It will also occasionally be used
|
||||
to test various failure scenarios, and there are no availability or service guarantees.
|
||||
|
||||
Develop a Program with Reticulum
|
||||
===========================================
|
||||
@@ -150,8 +265,11 @@ installing Reticulum or programs that depend on Reticulum.
|
||||
Reticulum on Android
|
||||
==============================================
|
||||
Reticulum can be used on Android in different ways. The easiest way to get
|
||||
started is using the `Termux app <https://termux.com/>`_, at the time of writing
|
||||
available on `F-droid <https://f-droid.org>`_.
|
||||
started is using an app like `Sideband <https://unsigned.io/sideband>`_.
|
||||
|
||||
For more control and features, you can use Reticulum and related programs via
|
||||
the `Termux app <https://termux.com/>`_, at the time of writing available on
|
||||
`F-droid <https://f-droid.org>`_.
|
||||
|
||||
Termux is a terminal emulator and Linux environment for Android based devices,
|
||||
which includes the ability to use many different programs and libraries,
|
||||
@@ -191,3 +309,22 @@ From within Termux, execute the following:
|
||||
It is also possible to include Reticulum in apps compiled and distributed as
|
||||
Android APKs. A detailed tutorial and example source code will be included
|
||||
here at a later point.
|
||||
|
||||
Adding Radio Interfaces
|
||||
==============================================
|
||||
Once you have Reticulum installed and working, you can add radio interfaces with
|
||||
any compatible hardware you have available. For information on how to configure
|
||||
this, see the :ref:`Interfaces<interfaces-main>` section of this manual.
|
||||
|
||||
A range of common LoRa development boards and transceiver modules can be used
|
||||
as interfaces with Reticulum. You can refer to the following external resources
|
||||
for more information:
|
||||
|
||||
* `How To Make Your Own RNodes <https://unsigned.io/how-to-make-your-own-rnodes/>`_
|
||||
* `Installing RNode Firmware on Compatible LoRa Devices <https://unsigned.io/installing-rnode-firmware-on-t-beam-and-lora32-devices/>`_
|
||||
* `Private, Secure and Uncensorable Messaging Over a LoRa Mesh <https://unsigned.io/private-messaging-over-lora/>`_
|
||||
* `RNode Firmware <https://github.com/markqvist/RNode_Firmware/>`_
|
||||
|
||||
If you have communications hardware that you think would be suitable for use with Reticulum,
|
||||
you are welcome to head over to the `GitHub discussion pages <https://github.com/markqvist/Reticulum/discussions>`_
|
||||
and propose adding an interface for the hardware.
|
||||
+237
-103
@@ -18,6 +18,72 @@ For a high-level overview of how networks can be formed over different interface
|
||||
types, have a look at the :ref:`Building Networks<networks-main>` chapter of this
|
||||
manual.
|
||||
|
||||
|
||||
.. _interfaces-options:
|
||||
|
||||
Common Interface Options
|
||||
========================
|
||||
|
||||
A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.
|
||||
|
||||
|
||||
* | The ``enabled`` option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to ``False``. For any
|
||||
interface to be brought up, the ``enabled`` option
|
||||
must be set to ``True`` or ``Yes``.
|
||||
|
||||
* | The ``mode`` option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.
|
||||
|
||||
- The default value is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is available.
|
||||
|
||||
- In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.
|
||||
|
||||
* | The ``outgoing`` option sets whether an interface is allowed
|
||||
to transmit. Defaults to ``True``. If set to ``False`` or ``No``
|
||||
the interface will only receive data, and never transmit.
|
||||
|
||||
* | The ``network_name`` option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.
|
||||
|
||||
* | The ``passphrase`` option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
``network_name`` option, or be used alone.
|
||||
|
||||
* | The ``ifac_size`` option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.
|
||||
|
||||
* | The ``announce_cap`` option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between ``1`` and ``100``.
|
||||
|
||||
* | The ``bitrate`` option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn't report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the ``bitrate``
|
||||
option, to set the interface speed in *bits per second*.
|
||||
|
||||
|
||||
.. _interfaces-auto:
|
||||
|
||||
Auto Interface
|
||||
@@ -39,7 +105,6 @@ system, which should be enabled by default in almost all OSes.
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# You can create multiple isolated Reticulum
|
||||
# networks on the same physical LAN by
|
||||
@@ -69,7 +134,6 @@ the discovery scope by setting it to one of ``link``, ``admin``, ``site``,
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# Configure global discovery
|
||||
|
||||
@@ -81,8 +145,175 @@ the discovery scope by setting it to one of ``link``, ``admin``, ``site``,
|
||||
discovery_port = 48555
|
||||
data_port = 49555
|
||||
|
||||
*Please Note!* If you use the Auto Interface, you will need the Python module
|
||||
``netifaces`` installed on your system. You can install it with ``pip3 install netifaces``.
|
||||
|
||||
.. _interfaces-i2p:
|
||||
|
||||
I2P Interface
|
||||
=============
|
||||
|
||||
The I2P interface lets you connect Reticulum instances over the
|
||||
`Invisible Internet Protocol <https://i2pd.website>`_. This can be
|
||||
especially useful in cases where you want to host a globally reachable
|
||||
Reticulum instance, but do not have access to any public IP addresses,
|
||||
have a frequently changing IP address, or have firewalls blocking
|
||||
inbound traffic.
|
||||
|
||||
Using the I2P interface, you will get a globally reachable, portable
|
||||
and persistent I2P address that your Reticulum instance can be reached
|
||||
at.
|
||||
|
||||
To use the I2P interface, you must have an I2P router running
|
||||
on your system. The easiest way to acheive this is to download and
|
||||
install the `latest release <https://github.com/PurpleI2P/i2pd/releases/latest>`_
|
||||
of the ``ì2pd`` package. For more details about I2P, see the
|
||||
`geti2p.net website <https://geti2p.net/en/about/intro>`_.`
|
||||
|
||||
When an I2P router is running on your system, you can simply add
|
||||
an I2P interface to reticulum:
|
||||
|
||||
.. code::
|
||||
|
||||
[[I2P]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
connectable = yes
|
||||
|
||||
On the first start, Reticulum will generate a new I2P address for the
|
||||
interface and start listening for inbound traffic on it. This can take
|
||||
a while the first time, especially if your I2P router was also just
|
||||
started, and is not yet well-connected to the I2P network. When ready,
|
||||
you should see I2P base32 address printed to your log file. You can
|
||||
also inspect the status of the interface using the ``rnstatus`` utility.
|
||||
|
||||
To connect to other Reticulum instances over I2P, just add a comma-separated
|
||||
list of I2P base32 addresses to the ``peers`` option of the interface:
|
||||
|
||||
.. code::
|
||||
|
||||
[[I2P]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
connectable = yes
|
||||
peers = 5urvjicpzi7q3ybztsef4i5ow2aq4soktfj7zedz53s47r54jnqq.b32.i2p
|
||||
|
||||
It can take anywhere from a few seconds to a few minutes to establish
|
||||
I2P connections to the desired peers, so Reticulum handles the process
|
||||
in the background, and will output relevant events to the log.
|
||||
|
||||
**Please Note!** While the I2P interface is the simplest way to use
|
||||
Reticulum over I2P, it is also possible to tunnel the TCP server and
|
||||
client interfaces over I2P manually. This can be useful in situations
|
||||
where more control is needed, but requires manual tunnel setup through
|
||||
the I2P daemon configuration.
|
||||
|
||||
It is important to note that the two methods are *interchangably compatible*.
|
||||
You can use the I2PInterface to connect to a TCPServerInterface that
|
||||
was manually tunneled over I2P, for example. This offers a high degree
|
||||
of flexibility in network setup, while retaining ease of use in simpler
|
||||
use-cases.
|
||||
|
||||
|
||||
.. _interfaces-tcps:
|
||||
|
||||
TCP Server Interface
|
||||
====================
|
||||
|
||||
The TCP Server interface is suitable for allowing other peers to connect over
|
||||
the Internet or private IP networks. When a TCP server interface has been
|
||||
configured, other Reticulum peers can connect to it with a TCP Client interface.
|
||||
|
||||
.. code::
|
||||
|
||||
# This example demonstrates a TCP server interface.
|
||||
# It will listen for incoming connections on the
|
||||
# specified IP address and port number.
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = True
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
|
||||
# Alternatively you can bind to a specific IP
|
||||
|
||||
# listen_ip = 10.0.0.88
|
||||
# listen_port = 4242
|
||||
|
||||
# Or a specific network device
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
**Please Note!** The TCP interfaces support tunneling over I2P, but to do so reliably,
|
||||
you must use the i2p_tunneled option:
|
||||
|
||||
.. code::
|
||||
|
||||
[[TCP Server on I2P]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = yes
|
||||
listen_ip = 127.0.0.1
|
||||
listen_port = 5001
|
||||
i2p_tunneled = yes
|
||||
|
||||
.. _interfaces-tcpc:
|
||||
|
||||
TCP Client Interface
|
||||
====================
|
||||
|
||||
To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface. The
|
||||
# target_host can either be an IP address or a hostname.
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
It is also possible to use this interface type to connect via other programs
|
||||
or hardware devices that expose a KISS interface on a TCP port, for example
|
||||
software-based soundmodems. To do this, use the ``kiss_framing`` option:
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface that connects
|
||||
# to a software TNC soundmodem on a KISS over TCP port.
|
||||
|
||||
[[TCP KISS Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
kiss_framing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 8001
|
||||
|
||||
**Caution!** Only use the KISS framing option when connecting to external devices
|
||||
and programs like soundmodems and similar over TCP. When using the
|
||||
``TCPClientInterface`` in conjunction with the ``TCPServerInterface`` you should
|
||||
never enable ``kiss_framing``, since this will disable internal reliability and
|
||||
recovery mechanisms that greatly improves performance over unreliable and
|
||||
intermittent TCP links.
|
||||
|
||||
**Please Note!** The TCP interfaces support tunneling over I2P, but to do so reliably,
|
||||
you must use the i2p_tunneled option:
|
||||
|
||||
.. code::
|
||||
|
||||
[[TCP Client over I2P]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = yes
|
||||
target_host = 127.0.0.1
|
||||
target_port = 5001
|
||||
i2p_tunneled = yes
|
||||
|
||||
|
||||
.. _interfaces-udp:
|
||||
@@ -113,7 +344,7 @@ pre-existing LAN.
|
||||
[[Default UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
forward_ip = 255.255.255.255
|
||||
@@ -149,93 +380,6 @@ pre-existing LAN.
|
||||
# forward_ip = 10.55.0.16
|
||||
# forward_port = 4242
|
||||
|
||||
*Please Note!* If you use the ``device`` option, you will need the Python module
|
||||
``netifaces`` installed on your system. You can install it with ``pip3 install netifaces``.
|
||||
|
||||
.. _interfaces-tcps:
|
||||
|
||||
TCP Server Interface
|
||||
====================
|
||||
|
||||
The TCP Server interface is suitable for allowing other peers to connect over
|
||||
the Internet or private IP networks. When a TCP server interface has been
|
||||
configured, other Reticulum peers can connect to it with a TCP Client interface.
|
||||
|
||||
.. code::
|
||||
|
||||
# This example demonstrates a TCP server interface.
|
||||
# It will listen for incoming connections on the
|
||||
# specified IP address and port number.
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
|
||||
# Alternatively you can bind to a specific IP
|
||||
|
||||
# listen_ip = 10.0.0.88
|
||||
# listen_port = 4242
|
||||
|
||||
# Or a specific network device
|
||||
|
||||
# device = eth0
|
||||
# port = 4242
|
||||
|
||||
*Please Note!* If you use the ``device`` option, you will need the Python module
|
||||
``netifaces`` installed on your system. You can install it with ``pip3 install netifaces``.
|
||||
|
||||
.. _interfaces-tcpc:
|
||||
|
||||
TCP Client Interface
|
||||
====================
|
||||
|
||||
To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface. The
|
||||
# target_host can either be an IP address or a hostname.
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
It is also possible to use this interface type to connect via other programs
|
||||
or hardware devices that expose a KISS interface on a TCP port, for example
|
||||
software-based soundmodems. To do this, use the ``kiss_framing`` option:
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface that connects
|
||||
# to a software TNC soundmodem on a KISS over TCP port.
|
||||
|
||||
[[TCP KISS Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
kiss_framing = True
|
||||
target_host = 127.0.0.1
|
||||
target_port = 8001
|
||||
|
||||
**Caution!** Only use the KISS framing option when connecting to external devices
|
||||
and programs like soundmodems and similar over TCP. When using the
|
||||
``TCPClientInterface`` in conjunction with the ``TCPServerInterface`` you should
|
||||
never enable ``kiss_framing``, since this will disable internal reliability and
|
||||
recovery mechanisms that greatly improves performance over unreliable and
|
||||
intermittent TCP links.
|
||||
|
||||
|
||||
.. _interfaces-rnode:
|
||||
|
||||
@@ -256,11 +400,6 @@ can be used, and offers full control over LoRa parameters.
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = True
|
||||
|
||||
# Allow transmit on interface. Setting
|
||||
# this to false will create a listen-
|
||||
# only interface.
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
|
||||
@@ -311,7 +450,6 @@ directly over a wire-pair, or for using devices such as data radios and lasers.
|
||||
[[Serial Interface]]
|
||||
type = SerialInterface
|
||||
interface_enabled = True
|
||||
outgoing = True
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
@@ -338,7 +476,6 @@ for station identification purposes.
|
||||
[[Packet Radio KISS Interface]]
|
||||
type = KISSInterface
|
||||
interface_enabled = True
|
||||
outgoing = true
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB1
|
||||
@@ -409,9 +546,6 @@ beaconing functionality described above.
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = True
|
||||
|
||||
# Allow transmit on interface.
|
||||
outgoing = True
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB2
|
||||
|
||||
@@ -443,4 +577,4 @@ beaconing functionality described above.
|
||||
# Whether to use KISS flow-control.
|
||||
# This is useful for modems with a
|
||||
# small internal packet buffer.
|
||||
flow_control = false
|
||||
flow_control = false
|
||||
|
||||
@@ -27,6 +27,11 @@ with Reticulum:
|
||||
While the adress space can support billions of endpoints, Reticulum is
|
||||
also very useful when just a few devices needs to communicate.
|
||||
|
||||
* | Low-bandwidth networks, like LoRa and packet radio, can interoperate and
|
||||
interconnect with much larger and higher bandwidth networks without issue.
|
||||
Reticulum automatically manages the flow of information to and from various
|
||||
network segments, and when bandwidth is limited, local traffic is prioritised.
|
||||
|
||||
* | Reticulum provides sender/initiator anonymity by default. There is no way
|
||||
to filter traffic or discriminate it based on the source of the traffic.
|
||||
|
||||
@@ -47,18 +52,32 @@ with Reticulum:
|
||||
transport node. Letting every node be a transport node will in most cases
|
||||
degrade the performance and reliability of the network.
|
||||
|
||||
In general terms, if a node is stationary, well-connected and kept running
|
||||
*In general terms, if a node is stationary, well-connected and kept running
|
||||
most of the time, it is a good candidate to be a transport node. For optimal
|
||||
performance, a network should contain the amount of transport nodes that
|
||||
provides connectivity to the intended area / topography, and not many more
|
||||
than that.
|
||||
than that.*
|
||||
|
||||
* | Reticulum is designed to work reliably in open, trustless environments. This
|
||||
means you can use it to create open-access networks, where participants can
|
||||
join and leave in an free and unorganised manner. This property allows an
|
||||
entirely new, and so far, mostly unexplored class of networked applications,
|
||||
where networks, and the information flow within them can form and dissolve
|
||||
organically.
|
||||
|
||||
* | You can just as easily create closed networks, since Reticulum allows you to
|
||||
add authentication to any interface. This means you can restrict access on
|
||||
any interface type, even when using legacy devices, such as modems. You can
|
||||
also mix authenticated and open interfaces on the same system. See the
|
||||
:ref:`Common Interface Options<interfaces-options>` section of the :ref:`Interfaces<interfaces-main>`
|
||||
chapter of this manual for information on how to set up interface authentication.
|
||||
|
||||
|
||||
Reticulum allows you to mix very different kinds of networking mediums into a
|
||||
unified mesh, or to keep everything within one medium. You could build a "virtual
|
||||
network" running entirely over the Internet, where all nodes communicate over TCP
|
||||
and UDP "channels". You could also build such a network using MQTT or ZeroMQ as
|
||||
the underlying carrier for Reticulum.
|
||||
and UDP "channels". You could also build such a network using other already-established
|
||||
communications channels as the underlying carrier for Reticulum.
|
||||
|
||||
However, most real-world networks will probably involve either some form of
|
||||
wireless or direct hardline communications. To allow Reticulum to communicate
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
+274
-175
@@ -3,20 +3,21 @@
|
||||
***********************
|
||||
Understanding Reticulum
|
||||
***********************
|
||||
This chapter will briefly describe the overall purpose and operating principles of Reticulum, a
|
||||
networking stack designed for reliable and secure communication over high-latency, low-bandwidth
|
||||
links. It should give you an overview of how the stack works, and an understanding of how to
|
||||
This chapter will briefly describe the overall purpose and operating principles of Reticulum.
|
||||
It should give you an overview of how the stack works, and an understanding of how to
|
||||
develop networked applications using Reticulum.
|
||||
|
||||
This document is not an exhaustive source of information on Reticulum, at least not yet. Currently,
|
||||
the best place to go for such information is the Python reference implementation of Reticulum, along
|
||||
with the code examples and API reference. It is however an essential resource to understanding the
|
||||
general principles of Reticulum, how to apply them when creating your own networks or software.
|
||||
This chapter is not an exhaustive source of information on Reticulum, at least not yet. Currently,
|
||||
the only complete repository, and final authority on how Reticulum actually functions, is the Python
|
||||
reference implementation and API reference. That being said, this chapter is an essential resource in
|
||||
understanding how Reticulum works from a high-level perspective, along with the general principles of
|
||||
Reticulum, and how to apply them when creating your own networks or software.
|
||||
|
||||
After reading this document, you should be well-equipped to understand how a Reticulum network
|
||||
operates, what it can achieve, and how you can use it yourself. If you want to help out with the
|
||||
development, this is also the place to start, since it will provide a pretty clear overview of the
|
||||
sentiments and the philosophy behind Reticulum.
|
||||
sentiments and the philosophy behind Reticulum, what problems it seeks to solve, and how it
|
||||
approaches those solutions.
|
||||
|
||||
.. _understanding-motivation:
|
||||
|
||||
@@ -25,34 +26,42 @@ Motivation
|
||||
|
||||
The primary motivation for designing and implementing Reticulum has been the current lack of
|
||||
reliable, functional and secure minimal-infrastructure modes of digital communication. It is my
|
||||
belief that it is highly desirable to create a cheap and reliable way to set up a wide-range digital
|
||||
communication network that can securely allow exchange of information between people and
|
||||
belief that it is highly desirable to create a reliable and efficient way to set up long-range digital
|
||||
communication networks that can securely allow exchange of information between people and
|
||||
machines, with no central point of authority, control, censorship or barrier to entry.
|
||||
|
||||
Almost all of the various networking systems in use today share a common limitation, namely that they
|
||||
require large amounts of coordination and trust to work, and to join the networks you need approval
|
||||
Almost all of the various networking systems in use today share a common limitation: They
|
||||
require large amounts of coordination and centralised trust and power to function. To join such networks, you need approval
|
||||
of gatekeepers in control. This need for coordination and trust inevitably leads to an environment of
|
||||
central control, where it's very easy for infrastructure operators or governments to control or alter
|
||||
traffic, and censor or persecute unwanted actors.
|
||||
traffic, and censor or persecute unwanted actors. It also makes it completely impossible to freely deploy
|
||||
and use networks at will, like one would use other common tools that enhance individual agency and freedom.
|
||||
|
||||
Reticulum aims to require as little coordination and trust as possible. In fact, the only
|
||||
“coordination” required is to know the characteristics of physical medium carrying Reticulum traffic.
|
||||
Reticulum aims to require as little coordination and trust as possible. It aims to make secure,
|
||||
anonymous and permissionless networking and information exchange a tool that anyone can just pick up and use.
|
||||
|
||||
Since Reticulum is completely medium agnostic, this could be whatever is best suited to the situation.
|
||||
In some cases, this might be 1200 baud packet radio links over VHF frequencies, in other cases it might
|
||||
be a microwave network using off-the-shelf radios. At the time of release of this document, the
|
||||
recommended setup for development and testing is using LoRa radio modules with an open source firmware
|
||||
(see the section :ref:`Reference System Setup<understanding-referencesystem>`), connected to a small
|
||||
computer like a Raspberry Pi. As an example, the default reference setup provides a channel capacity
|
||||
of 5.4 Kbps, and a usable direct node-to-node range of around 15 kilometers (indefinitely extendable
|
||||
by using multiple hops).
|
||||
Since Reticulum is completely medium agnostic, it can be used to build networks on whatever is best
|
||||
suited to the situation, or whatever you have available. In some cases, this might be packet radio
|
||||
links over VHF frequencies, in other cases it might be a 2.4 GHz
|
||||
network using off-the-shelf radios, or it might be using common LoRa development boards.
|
||||
|
||||
At the time of release of this document, the fastest and easiest setup for development and testing is using
|
||||
LoRa radio modules with an open source firmware (see the section :ref:`Reference Setup<understanding-referencesystem>`),
|
||||
connected to any kind of computer or mobile device that Reticulum can run on.
|
||||
|
||||
The ultimate aim of Reticulum is to allow anyone to be their own network operator, and to make it
|
||||
cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks.
|
||||
Reticulum **is not** *one network*, it **is a tool** to build *thousands of networks*.
|
||||
|
||||
Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
|
||||
.. _understanding-goals:
|
||||
|
||||
Goals
|
||||
=====
|
||||
|
||||
To be as widely usable and easy to use as possible, the following goals have been used to
|
||||
To be as widely usable and efficient to deploy as possible, the following goals have been used to
|
||||
guide the design of Reticulum:
|
||||
|
||||
|
||||
@@ -60,14 +69,14 @@ guide the design of Reticulum:
|
||||
Reticulum must be implemented with, and be able to run using only open source software. This is
|
||||
critical to ensuring the availability, security and transparency of the system.
|
||||
* **Hardware layer agnosticism**
|
||||
Reticulum shall be fully hardware agnostic, and shall be useable over a wide range
|
||||
Reticulum must be fully hardware agnostic, and shall be useable over a wide range of
|
||||
physical networking layers, such as data radios, serial lines, modems, handheld transceivers,
|
||||
wired ethernet, wifi, or anything else that can carry a digital data stream. Hardware made for
|
||||
dedicated Reticulum use shall be as cheap as possible and use off-the-shelf components, so
|
||||
it can be easily replicated.
|
||||
it can be easily modified and replicated by anyone interested in doing so.
|
||||
* **Very low bandwidth requirements**
|
||||
Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||
as *500 bps*.
|
||||
as *500 bits per second*.
|
||||
* **Encryption by default**
|
||||
Reticulum must use strong encryption by default for all communication.
|
||||
* **Initiator Anonymity**
|
||||
@@ -79,15 +88,15 @@ guide the design of Reticulum:
|
||||
frequency bands, and can provide functional long distance links in such conditions, for example
|
||||
by connecting a modem to a PMR or CB radio, or by using LoRa or WiFi modules.
|
||||
* **Supplied software**
|
||||
Apart from the core networking stack and API, that allows a developer to build
|
||||
applications with Reticulum, a basic communication suite using Reticulum must be
|
||||
implemented and released at the same time as Reticulum itself. This shall serve both as a
|
||||
functional communication suite, and as an example and learning resource to others wishing
|
||||
In addition to the core networking stack and API, that allows a developer to build
|
||||
applications with Reticulum, a basic set of Reticulum-based communication tools must be
|
||||
implemented and released along with Reticulum itself. These shall serve both as a
|
||||
functional, basic communication suite, and as an example and learning resource to others wishing
|
||||
to build applications with Reticulum.
|
||||
* **Ease of use**
|
||||
The reference implementation of Reticulum is written in Python, to make it easy to use
|
||||
and understand. A programmer with only basic experience should be able to use
|
||||
Reticulum in their own applications.
|
||||
Reticulum to write networked applications.
|
||||
* **Low cost**
|
||||
It shall be as cheap as possible to deploy a communication system based on Reticulum. This
|
||||
should be achieved by using cheap off-the-shelf hardware that potential users might already
|
||||
@@ -109,20 +118,29 @@ Reticulum uses the singular concept of *destinations*. Any application using Ret
|
||||
networking stack will need to create one or more destinations to receive data, and know the
|
||||
destinations it needs to send data to.
|
||||
|
||||
All destinations in Reticulum are represented internally as 10 bytes, derived from truncating a full
|
||||
All destinations in Reticulum are represented as a 10 byte hash, derived from truncating a full
|
||||
SHA-256 hash of identifying characteristics of the destination. To users, the destination addresses
|
||||
will be displayed as 10 bytes in hexadecimal representation, as in the following example: ``<80e29bf7cccaf31431b3>``.
|
||||
|
||||
The truncation size of 10 bytes (80 bits) for destinations has been choosen as a reasonable tradeoff between address space
|
||||
and packet overhead. The address space accomodated by this size can support many billions of
|
||||
simultaneously active devices on the same network, while keeping packet overhead low, which is
|
||||
essential on low-bandwidth networks. In the very unlikely case that this address space nears
|
||||
congestion, a one-line code change can upgrade the Reticulum address space all the way up to 256
|
||||
bits, ensuring the Reticulum address space could potentially support galactic-scale networks.
|
||||
This is obviusly complete and ridiculous over-allocation, and as such, the current 80 bits should
|
||||
be sufficient, even far into the future.
|
||||
|
||||
By default Reticulum encrypts all data using elliptic curve cryptography. Any packet sent to a
|
||||
destination is encrypted with a derived ephemeral key. Reticulum can also set up an encrypted
|
||||
channel to a destination with *Forward Secrecy* and *Initiator Anonymity* using a elliptic
|
||||
curve cryptography and ephemeral keys derived from a Diffie Hellman exchange on Curve25519. In
|
||||
Reticulum terminology, this is called a *Link*.
|
||||
Reticulum terminology, this is called a *Link*. The multi-hop transport, coordination, verification
|
||||
and reliability layers are fully autonomous and also based on elliptic curve cryptography.
|
||||
|
||||
Reticulum also offers symmetric key encryption for group-oriented communications, as well as
|
||||
unencrypted packets for broadcast purposes, or situations where you need the communication to be in
|
||||
plain text. The multi-hop transport, coordination, verification and reliability layers are fully
|
||||
autonomous and based on public key cryptography.
|
||||
plain text.
|
||||
|
||||
Reticulum can connect to a variety of interfaces such as radio modems, data radios and serial ports,
|
||||
and offers the possibility to easily tunnel Reticulum traffic over IP links such as the Internet or
|
||||
@@ -138,22 +156,30 @@ destinations. Reticulum uses three different basic destination types, and one sp
|
||||
|
||||
|
||||
* **Single**
|
||||
The *single* destination type is always identified by a unique public key. Any data sent to this
|
||||
The *single* destination type is the most common type in Reticulum, and should be used for
|
||||
most purposes. It is always identified by a unique public key. Any data sent to this
|
||||
destination will be encrypted using ephemeral keys derived from an ECDH key exchange, and will
|
||||
only be readable by the creator of the destination, who holds the corresponding private key.
|
||||
* **Group**
|
||||
The *group* destination type defines a symmetrically encrypted destination. Data sent to this
|
||||
destination will be encrypted with a symmetric key, and will be readable by anyone in
|
||||
possession of the key.
|
||||
* **Plain**
|
||||
A *plain* destination type is unencrypted, and suited for traffic that should be broadcast to a
|
||||
number of users, or should be readable by anyone. Traffic to a *plain* destination is not encrypted.
|
||||
Generally, *plain* destinations can be used for broadcast information intended to be public.
|
||||
Plain destinations are only reachable directly, and packets adressed to plain destinations are
|
||||
never transported over multiple hops in the network. To be transportable over multiple hops in Reticulum, information
|
||||
*must* be encrypted, since Reticulum uses the per-packet encryption to verify routing paths and
|
||||
keep them alive.
|
||||
* **Group**
|
||||
The *group* special destination type, that defines a symmetrically encrypted virtual destination.
|
||||
Data sent to this destination will be encrypted with a symmetric key, and will be readable by
|
||||
anyone in possession of the key, but as with the *plain* destination type, packets to this type
|
||||
of destination are not currently transported over multiple hops, although a planned upgrade
|
||||
to Reticulum will allow globally reachable *group* destinations.
|
||||
* **Link**
|
||||
A *link* is a special destination type, that serves as an abstract channel to a *single*
|
||||
destination, directly connected or over multiple hops. The *link* also offers reliability and
|
||||
more efficient encryption, forward secrecy, initiator anonymity, and as such can be useful even
|
||||
when a node is directly reachable.
|
||||
when a node is directly reachable. It also offers a more capable API and allows easily carrying
|
||||
out requests and responses, large data transfers and more.
|
||||
|
||||
.. _understanding-destinationnaming:
|
||||
|
||||
@@ -195,7 +221,7 @@ packet.
|
||||
In actual use of *single* destination naming, it is advisable not to use any uniquely identifying
|
||||
features in aspect naming. Aspect names should be general terms describing what kind of destination
|
||||
is represented. The uniquely identifying aspect is always acheived by the appending the public key,
|
||||
which expands the destination into a uniquely identifyable one.
|
||||
which expands the destination into a uniquely identifyable one. Reticulum does this automatically.
|
||||
|
||||
Any destination on a Reticulum network can be addressed and reached simply by knowning its
|
||||
destination hash (and public key, but if the public key is not known, it can be requested from the
|
||||
@@ -211,30 +237,32 @@ To recap, the different destination types should be used in the following situat
|
||||
When private communication between two or more endpoints is needed. Supports multiple hops
|
||||
indirectly, but must first be established through a *single* destination.
|
||||
* **Plain**
|
||||
When plain-text communication is desirable, for example when broadcasting information.
|
||||
When plain-text communication is desirable, for example when broadcasting information, or for local discovery purposes.
|
||||
|
||||
To communicate with a *single* destination, you need to know it’s public key. Any method for
|
||||
obtaining the public key is valid, but Reticulum includes a simple mechanism for making other
|
||||
nodes aware of your destinations public key, called the *announce*. It is also possible to request
|
||||
an unknown public key from the network, as all participating nodes serve as a distributed ledger
|
||||
an unknown public key from the network, as all transport instances serve as a distributed ledger
|
||||
of public keys.
|
||||
|
||||
Note that public key information can be shared and verified in many other ways than using the
|
||||
built-in *announce* functionality, and that it is therefore not required to use the announce/request
|
||||
Note that public key information can be shared and verified in other ways than using the
|
||||
built-in *announce* functionality, and that it is therefore not required to use the *announce* and *path request*
|
||||
functionality to obtain public keys. It is by far the easiest though, and should definitely be used
|
||||
if there is not a good reason for doing it differently.
|
||||
if there is not a very good reason for doing it differently.
|
||||
|
||||
.. _understanding-keyannouncements:
|
||||
|
||||
Public Key Announcements
|
||||
------------------------
|
||||
|
||||
An *announce* will send a special packet over any configured interfaces, containing all needed
|
||||
An *announce* will send a special packet over any relevant interfaces, containing all needed
|
||||
information about the destination hash and public key, and can also contain some additional,
|
||||
application specific data. The entire packet is signed by the sender to ensure authenticity. It is not
|
||||
required to use the announce functionality, but in many cases it will be the simplest way to share
|
||||
public keys on the network. As an example, an announce in a simple messenger application might
|
||||
contain the following information:
|
||||
public keys on the network. The announce mechanism also serves to establish end-to-end connectivity
|
||||
to the announced destination, as the announce propagates through the network.
|
||||
|
||||
As an example, an announce in a simple messenger application might contain the following information:
|
||||
|
||||
|
||||
* The announcers destination hash
|
||||
@@ -247,13 +275,21 @@ With this information, any Reticulum node that receives it will be able to recon
|
||||
destination to securely communicate with that destination. You might have noticed that there is one
|
||||
piece of information lacking to reconstruct full knowledge of the announced destination, and that is
|
||||
the aspect names of the destination. These are intentionally left out to save bandwidth, since they
|
||||
will be implicit in almost all cases. If a destination name is not entirely implicit, information can be
|
||||
included in the application specific data part that will allow the receiver to infer the naming.
|
||||
will be implicit in almost all cases. The receiving application will already know them. If a destination
|
||||
name is not entirely implicit, information can be included in the application specific data part that
|
||||
will allow the receiver to infer the naming.
|
||||
|
||||
It is important to note that announces will be forwarded throughout the network according to a
|
||||
certain pattern. This will be detailed in the section
|
||||
:ref:`The Announce Mechanism in Detail<understanding-announce>`.
|
||||
|
||||
In Reticulum, destinations are allowed to move around the network at will. This is very different from
|
||||
protocols such as IP, where an address is always expected to stay within the network segment it was assigned in.
|
||||
This limitation does not exist in Reticulum, and any destination is *completely portable* over the entire topography
|
||||
of the network, and *can even be moved to other Reticulum networks* than the one it was created in, and
|
||||
still become reachable. To update it's reachability, a destination simply needs to send an announce on any
|
||||
networks it is part of. After a short while, it will be globally reachable in the network.
|
||||
|
||||
Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic.
|
||||
|
||||
.. _understanding-identities:
|
||||
@@ -262,21 +298,22 @@ Identities
|
||||
----------
|
||||
|
||||
In Reticulum, an *identity* does not necessarily represent a personal identity, but is an abstraction that
|
||||
can represent any kind of *verified entity*. This could very well be a person, but it could also be the
|
||||
can represent any kind of *verifiable entity*. This could very well be a person, but it could also be the
|
||||
control interface of a machine, a program, robot, computer, sensor or something else entirely. In
|
||||
general, any kind of agent that can act, or be acted upon, or store or manipulate information, can be
|
||||
represented as an identity.
|
||||
represented as an identity. An *identity* can be used to create any number of destinations.
|
||||
|
||||
As we have seen, a *single* destination will always have an *identity* tied to it, but not *plain* or *group*
|
||||
A *single* destination will always have an *identity* tied to it, but not *plain* or *group*
|
||||
destinations. Destinations and identities share a multilateral connection. You can create a
|
||||
destination, and if it is not connected to an identity upon creation, it will just create a new one to use
|
||||
automatically. This may be desirable in some situations, but often you will probably want to create
|
||||
the identity first, and then link it to created destinations.
|
||||
the identity first, and then use it to create new destinations.
|
||||
|
||||
Building upon the simple messenger example, we could use an identity to represent the user of the
|
||||
application. Destinations created will then be linked to this identity to allow communication to
|
||||
reach the user. In all cases it is of great importance to store the private keys associated with any
|
||||
Reticulum Identity securely and privately.
|
||||
As an example, we could use an identity to represent the user of a messaging application.
|
||||
Destinations can then be created by this identity to allow communication to reach the user.
|
||||
In all cases it is of great importance to store the private keys associated with any
|
||||
Reticulum Identity securely and privately, since obtaining access to the identity keys equals
|
||||
obtaining access and controlling reachability to any destinations created by that identity.
|
||||
|
||||
.. _understanding-gettingfurther:
|
||||
|
||||
@@ -295,57 +332,73 @@ In the following sections, two concepts that allow this will be introduced, *pat
|
||||
Reticulum Transport
|
||||
===================
|
||||
|
||||
The term routing has been purposefully avoided until now. The current methods of routing used in IP-based
|
||||
networks are fundamentally incompatible with the physical link types that Reticulum was designed to handle.
|
||||
These routing methodologies assume trust at the physical layer, and often needs a lot more bandwidth than
|
||||
Reticulum can assume is available.
|
||||
The methods of routing used in traditional networks are fundamentally incompatible with the physical medium
|
||||
types and circumstances that Reticulum was designed to handle. These mechanisms mostly assume trust at the physical layer,
|
||||
and often needs a lot more bandwidth than Reticulum can assume is available. Since Reticulum is designed to
|
||||
survive running over open radio spectrum, no such trust can be assumed, and bandwidth is often very limited.
|
||||
|
||||
Since Reticulum is designed to run over open radio spectrum, no such trust exists, and bandwidth is often
|
||||
very limited. Existing routing protocols like BGP or OSPF carry too much overhead to be practically
|
||||
useable over bandwidth-limited, high-latency links.
|
||||
|
||||
To overcome such challenges, Reticulum’s *Transport* system uses public-key cryptography to
|
||||
implement the concept of *paths* that allow discovery of how to get information to a certain
|
||||
To overcome such challenges, Reticulum’s *Transport* system uses asymmetric elliptic curve cryptography to
|
||||
implement the concept of *paths* that allow discovery of how to get information closer to a certain
|
||||
destination. It is important to note that no single node in a Reticulum network knows the complete
|
||||
path to a destination. Every Transport node participating in a Reticulum network will only
|
||||
know what the most direct way to get a packet one hop closer to it's destination is.
|
||||
know the most direct way to get a packet one hop closer to it's destination.
|
||||
|
||||
|
||||
.. _understanding-nodetypes:
|
||||
|
||||
Node Types
|
||||
----------
|
||||
|
||||
Currently, Reticulum distinguishes between two types of network nodes. All nodes on a Reticulum network
|
||||
are *Reticulum Instances*, and some are alo *Transport Nodes*. If a system running Reticulum is fixed in
|
||||
one place, and is intended to be kept available most of the time, it is a good contender to be a *Transport Node*.
|
||||
|
||||
Any Reticulum Instance can become a Transport Node by enabling it in the configuration.
|
||||
This distinction is made by the user configuring the node, and is used to determine what nodes on the
|
||||
network will help forward traffic, and what nodes rely on other nodes for wider connectivity.
|
||||
|
||||
If a node is an *Instance* it should be given the configuration directive ``enable_transport = No``, which
|
||||
is the default setting.
|
||||
|
||||
If it is a *Transport Node*, it should be given the configuration directive ``enable_transport = Yes``.
|
||||
|
||||
|
||||
.. _understanding-announce:
|
||||
|
||||
The Announce Mechanism in Detail
|
||||
--------------------------------
|
||||
|
||||
When an *announce* is transmitted by a node, it will be forwarded by any node receiving it, but
|
||||
according to some specific rules:
|
||||
When an *announce* for a destination is transmitted by from a Reticulum instance, it will be forwarded by
|
||||
any transport node receiving it, but according to some specific rules:
|
||||
|
||||
|
||||
* | If this exact announce has already been received before, ignore it.
|
||||
|
||||
* | If not, record into a table which node the announce was received from, and how many times in
|
||||
* | If not, record into a table which Transport Node the announce was received from, and how many times in
|
||||
total it has been retransmitted to get here.
|
||||
|
||||
* | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is
|
||||
set to 18.
|
||||
* | If the announce has been retransmitted *m+1* times, it will not be forwarded any more. By default, *m* is
|
||||
set to 128.
|
||||
|
||||
* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, and *h* is the amount of times this packet has already been forwarded.
|
||||
* | After a randomised delay, the announce will be retransmitted on all interfaces that have bandwidth
|
||||
available for processing announces. By default, the maximum bandwidth allocation for processing
|
||||
announces is set at 2%, but can be configured on a per-interface basis.
|
||||
|
||||
* | The packet will be given a priority *p = 1/d*.
|
||||
* | If any given interface does not have enough bandwidth available for retransmitting the announce,
|
||||
the announce will be assigned a priority inversely proportional to it's hop count, and be inserted
|
||||
into a queue managed by the interface.
|
||||
|
||||
* | If at least *d* seconds has passed since the announce was received, and no other packets with a
|
||||
priority higher than *p* are waiting in the queue (see Packet Prioritisation), and the channel is
|
||||
not utilized by other traffic, the announce will be forwarded.
|
||||
* | When the interface has bandwidth available for processing an announce, it will prioritise announces
|
||||
for destinations that are closest in terms of hops, thus prioritising reachability and connectivity
|
||||
of local nodes, even on slow networks that connect to wider and faster networks.
|
||||
|
||||
* | If no other nodes are heard retransmitting the announce with a greater hop count than when
|
||||
it left this node, transmitting it will be retried *r* times. By default, *r* is set to 1. Retries
|
||||
follow same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` +
|
||||
t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
|
||||
node to retransmit the packet, plus a random window. By default, *t* is set to 10 seconds, and the
|
||||
random window *rw* is set to 10 seconds.
|
||||
* | After the announce has been re-transmitted, and if no other nodes are heard retransmitting the announce
|
||||
with a greater hop count than when it left this node, transmitting it will be retried *r* times. By default,
|
||||
*r* is set to 1.
|
||||
|
||||
* | If a newer announce from the same destination arrives, while an identical one is already in
|
||||
the queue, the newest announce is discarded. If the newest announce contains different
|
||||
application specific data, it will replace the old announce, but will use *d* and *p* of the old
|
||||
announce.
|
||||
* | If a newer announce from the same destination arrives, while an identical one is already waiting
|
||||
to be transmitted, the newest announce is discarded. If the newest announce contains different
|
||||
application specific data, it will replace the old announce.
|
||||
|
||||
Once an announce has reached a node in the network, any other node in direct contact with that
|
||||
node will be able to reach the destination the announce originated from, simply by sending a packet
|
||||
@@ -353,11 +406,16 @@ addressed to that destination. Any node with knowledge of the announce will be a
|
||||
packet towards the destination by looking up the next node with the shortest amount of hops to the
|
||||
destination.
|
||||
|
||||
According to these rules and default constants, an announce will propagate throughout the network
|
||||
in a predictable way. In an example network utilising the default constants, and with an average link
|
||||
distance of *Lavg =* 15 kilometers, an announce will be able to propagate outwards to a radius of 180
|
||||
kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3
|
||||
days.
|
||||
According to these rules, an announce will propagate throughout the network in a predictable way,
|
||||
and make the announced destination reachable in a short amount of time. Fast networks that have the
|
||||
capacity to process many announces can reach full convergence very quickly, even when constantly adding
|
||||
new destinations. Slower segments of such networks might take a bit longer to gain full knowledge about
|
||||
the wide and fast networks they are connected to, but can still do so over time, while prioritising full
|
||||
and quickly converging end-to-end connectivity for their local, slower segments.
|
||||
|
||||
In general, even extremely complex networks, that utilize the maximum 128 hops will converge to full
|
||||
end-to-end connectivity in about one minute, given there is enough bandwidth available to process
|
||||
the required amount of announces.
|
||||
|
||||
.. _understanding-paths:
|
||||
|
||||
@@ -404,7 +462,7 @@ For exchanges of small amounts of information, Reticulum offers the *Packet* API
|
||||
For exchanges of larger amounts of data, or when longer sessions of bidirectional communication is desired, Reticulum offers the *Link* API. To establish a *link*, the following process is employed:
|
||||
|
||||
* | First, the node that wishes to establish a link will send out a special packet, that
|
||||
traverses the network and locates the desired destination. Along the way, the nodes that
|
||||
traverses the network and locates the desired destination. Along the way, the Transport Nodes that
|
||||
forward the packet will take note of this *link request*.
|
||||
|
||||
* | Second, if the destination accepts the *link request* , it will send back a packet that proves the
|
||||
@@ -415,15 +473,19 @@ For exchanges of larger amounts of data, or when longer sessions of bidirectiona
|
||||
* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will
|
||||
remember the *link* , and it can subsequently be used by referring to a hash representing it.
|
||||
|
||||
* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an
|
||||
efficiently encrypted tunnel between the two nodes, using elliptic curve cryptography. As such,
|
||||
this mode of communication is preferred, even for situations when nodes can directly communicate,
|
||||
when the amount of data to be exchanged numbers in the tens of packets.
|
||||
* | As a part of the *link request*, an Elliptic Curve Diffie-Hellman key exchange takes place, that sets up an
|
||||
efficiently encrypted tunnel between the two nodes. As such, this mode of communication is preferred,
|
||||
even for situations when nodes can directly communicate, when the amount of data to be exchanged numbers
|
||||
in the tens of packets, or whenever the use of the more advanced API functions is desired.
|
||||
|
||||
* | When a *link* has been set up, it automatically provides message receipt functionality, through
|
||||
the same *proof* mechanism discussed before, so the sending node can obtain verified confirmation
|
||||
that the information reached the intended recipient.
|
||||
|
||||
* | Once the *link* has been set up, the initiator can remain anonymous, or choose to authenticate towards
|
||||
the destination using a Reticulum Identity. This authentication is happening inside the encrypted
|
||||
link, and is only revealed to the verified destination, and no intermediaries.
|
||||
|
||||
In a moment, we will discuss the details of how this methodology is implemented, but let’s first
|
||||
recap what purposes this methodology serves. We first ensure that the node answering our request
|
||||
is actually the one we want to communicate with, and not a malicious actor pretending to be so.
|
||||
@@ -518,57 +580,62 @@ or stream data directly from files.
|
||||
|
||||
.. _understanding-referencesystem:
|
||||
|
||||
Reference System Setup
|
||||
Reference Setup
|
||||
======================
|
||||
|
||||
This section will detail the recommended *Reference System Setup* for Reticulum. It is important to
|
||||
note that Reticulum is designed to be usable over more or less any medium that allows you to send
|
||||
and receive data in a digital form, and satisfies some very low minimum requirements. The
|
||||
communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 1000 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
Reticulum software should be able to run on more or less any hardware that can provide a Python 3.x
|
||||
This section will detail a recommended *Reference Setup* for Reticulum. It is important to
|
||||
note that Reticulum is designed to be usable on more or less any computing device, and over more
|
||||
or less any medium that allows you to send and receive data, which satisfies some very low
|
||||
minimum requirements.
|
||||
|
||||
The communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
||||
runtime environment.
|
||||
|
||||
That being said, the reference setup has been outlined to provide a common platform for anyone
|
||||
That being said, this reference setup has been outlined to provide a common platform for anyone
|
||||
who wants to help in the development of Reticulum, and for everyone who wants to know a
|
||||
recommended setup to get started. A reference system consists of three parts:
|
||||
recommended setup to get started experimenting. A reference system consists of three parts:
|
||||
|
||||
* **A channel access device**
|
||||
Or *CAD* , in short, provides access to the physical medium whereupon the communication
|
||||
* **An Interface Device**
|
||||
Which provides access to the physical medium whereupon the communication
|
||||
takes place, for example a radio with an integrated modem. A setup with a separate modem
|
||||
connected to a radio would also be termed a “channel access device”.
|
||||
* **A host device**
|
||||
Some sort of computing device that can run the necessary software, communicates with the
|
||||
channel access device, and provides user interaction.
|
||||
* **A software stack**
|
||||
connected to a radio would also be an interface device.
|
||||
* **A Host Device**
|
||||
Some sort of computing device that can run the necessary software, communicate with the
|
||||
interface device, and provide user interaction.
|
||||
* **A Software Stack**
|
||||
The software implementing the Reticulum protocol and applications using it.
|
||||
|
||||
The reference setup can be considered a relatively stable platform to develop on, and also to start
|
||||
building networks on. While details of the implementation might change at the current stage of
|
||||
building networks or applications on. While details of the implementation might change at the current stage of
|
||||
development, it is the goal to maintain hardware compatibility for as long as entirely possible, and
|
||||
the current reference setup has been determined to provide a functional platform for many years
|
||||
into the future. The current Reference System Setup is as follows:
|
||||
|
||||
|
||||
* **Channel Access Device**
|
||||
* **Interface Device**
|
||||
A data radio consisting of a LoRa radio module, and a microcontroller with open source
|
||||
firmware, that can connect to host devices via USB. It operates in either the 430, 868 or 900
|
||||
MHz frequency bands. More details can be found on the `RNode Page <https://unsigned.io/rnode>`_.
|
||||
* **Host device**
|
||||
* **Host Device**
|
||||
Any computer device running Linux and Python. A Raspberry Pi with a Debian based OS is
|
||||
recommended.
|
||||
* **Software stack**
|
||||
The current Reference Implementation Release of Reticulum, running on a Debian based
|
||||
* **Software Stack**
|
||||
The most recently released Python Implementation of Reticulum, running on a Debian based
|
||||
operating system.
|
||||
|
||||
It is very important to note, that the reference channel access device **does not** use the LoRaWAN
|
||||
standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will
|
||||
need a plain LoRa radio module connected to an MCU with the correct firmware. Full details on how to
|
||||
To avoid confusion, it is very important to note, that the reference interface device **does not**
|
||||
use the LoRaWAN standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will
|
||||
need a plain LoRa radio module connected to an controller with the correct firmware. Full details on how to
|
||||
get or make such a device is available on the `RNode Page <https://unsigned.io/rnode>`_.
|
||||
|
||||
With the current reference setup, it should be possible to get on a Reticulum network for around 100$
|
||||
even if you have none of the hardware already, and need to purchase everything.
|
||||
|
||||
This reference setup is of course just a recommendation for getting started easily, and you should
|
||||
tailor it to your own specific needs, or whatever hardware you have available.
|
||||
|
||||
.. _understanding-protocolspecifics:
|
||||
|
||||
Protocol Specifics
|
||||
@@ -579,20 +646,6 @@ Reticulum, but non critical in understanding how the protocol works on a general
|
||||
treated more as a reference than as essential reading.
|
||||
|
||||
|
||||
Node Types
|
||||
----------
|
||||
|
||||
Currently Reticulum defines two node types, the *Station* and the *Peer*. A node is a *station* if it fixed
|
||||
in one place, and if it is intended to be kept online most of the time. Otherwise the node is a *peer*.
|
||||
|
||||
This distinction is made by the user configuring the node, and is used to determine what nodes on the
|
||||
network will help forward traffic, and what nodes rely on other nodes for connectivity.
|
||||
|
||||
If a node is a *Peer* it should be given the configuration directive ``enable_transport = No``.
|
||||
|
||||
If it is a *Station*, it should be given the configuration directive ``enable_transport = Yes``.
|
||||
|
||||
|
||||
Packet Prioritisation
|
||||
---------------------
|
||||
|
||||
@@ -601,10 +654,29 @@ on a first-come, first-serve basis. Announce re-transmission are handled accordi
|
||||
times and priorities described earlier in this chapter.
|
||||
|
||||
|
||||
Interface Access Codes
|
||||
----------------------
|
||||
|
||||
Reticulum can create named virtual networks, and networks that are only accessible by knowing a preshared
|
||||
passphrase. The configuration of this is detailed in the :ref:`Common Interface Options<interfaces-options>`
|
||||
section. To implement these feature, Reticulum uses the concept of Interface Access Codes, that are calculated
|
||||
and verified per packet.
|
||||
|
||||
An interface with a named virtual network or passphrase authentication enabled will derive a shared Ed25519
|
||||
signing identity, and for every outbound packet generate a signature of the entire packet. This signature is
|
||||
then inserted into the packet as an Interface Access Code before transmission. Depending on the speed and
|
||||
capabilities of the interface, the IFAC can be the full 512-bit Ed25519 signature, or a truncated version.
|
||||
Configured IFAC length can be inspected for all interfaces with the ``rnstatus`` utility.
|
||||
|
||||
Upon receipt, the interface will check that the signature matches the expected value, and drop the packet if it
|
||||
does not. This ensures that only packets sent with the correct naming and/or passphrase parameters are allowed to
|
||||
pass onto the network.
|
||||
|
||||
|
||||
.. _understanding-packetformat:
|
||||
|
||||
Binary Packet Format
|
||||
--------------------
|
||||
Wire Format
|
||||
-----------
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
@@ -615,9 +687,14 @@ Binary Packet Format
|
||||
[HEADER 2 bytes] [ADDRESSES 10/20 bytes] [CONTEXT 1 byte] [DATA 0-477 bytes]
|
||||
|
||||
* The HEADER field is 2 bytes long.
|
||||
* Byte 1: [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 2: Number of hops
|
||||
|
||||
* Interface Access Code field if the IFAC flag was set.
|
||||
* The length of the Interface Access Code can vary from
|
||||
1 to 64 bytes according to physical interface
|
||||
capabilities and configuration.
|
||||
|
||||
* The ADDRESSES field contains either 1 or 2 addresses.
|
||||
* Each address is 10 bytes long.
|
||||
* The Header Type flag in the HEADER field determines
|
||||
@@ -630,12 +707,16 @@ Binary Packet Format
|
||||
* The DATA field is between 0 and 477 bytes.
|
||||
* It contains the packets data payload.
|
||||
|
||||
IFAC Flag
|
||||
-----------------
|
||||
open 0 Packet for publically accessible interface
|
||||
authenticated 1 Interface authentication is included in packet
|
||||
|
||||
|
||||
Header Types
|
||||
-----------------
|
||||
type 1 00 Two byte header, one 10 byte address field
|
||||
type 2 01 Two byte header, two 10 byte address fields
|
||||
type 3 10 Reserved
|
||||
type 4 11 Reserved
|
||||
type 1 0 Two byte header, one 10 byte address field
|
||||
type 2 1 Two byte header, two 10 byte address fields
|
||||
|
||||
|
||||
Propagation Types
|
||||
@@ -664,42 +745,60 @@ Binary Packet Format
|
||||
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD
|
||||
HEADER FIELD DESTINATION FIELDS CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ________________|________________ ________|______ __|_
|
||||
| | | | | | | |
|
||||
01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
| | | | |
|
||||
| | | | +-- Hops = 4
|
||||
| | | +------- Packet Type = DATA
|
||||
| | +--------- Destination Type = SINGLE
|
||||
| +----------- Propagation Type = TRANSPORT
|
||||
+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
01010000 00000100 [HASH1, 10 bytes] [HASH2, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 4
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = TRANSPORT
|
||||
|+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
+- Packet Example -+
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD
|
||||
HEADER FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ _______|_______ ________|______ __|_
|
||||
| | | | | | | |
|
||||
00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
| | | | |
|
||||
| | | | +-- Hops = 7
|
||||
| | | +------- Packet Type = DATA
|
||||
| | +--------- Destination Type = SINGLE
|
||||
| +----------- Propagation Type = BROADCAST
|
||||
+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
00000000 00000111 [HASH1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
+- Packet Example -+
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size including all fields.
|
||||
HEADER FIELD IFAC FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ______|______ _______|_______ ________|______ __|_
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 10 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 0
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = ENABLED
|
||||
|
||||
- Path Request : 33 bytes
|
||||
- Announce : 151 bytes
|
||||
- Link Request : 77 bytes
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size counting all fields including headers,
|
||||
but excluding any interface access codes.
|
||||
|
||||
- Path Request : 33 bytes
|
||||
- Announce : 151 bytes
|
||||
- Link Request : 77 bytes
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
+46
-24
@@ -6,16 +6,18 @@ Using Reticulum on Your System
|
||||
|
||||
Reticulum is not installed as a driver or kernel module, as one might expect
|
||||
of a networking stack. Instead, Reticulum is distributed as a Python module.
|
||||
This means that no special privileges are required to install or use it.
|
||||
This means that no special privileges are required to install or use it. It
|
||||
is also very light-weight, and easy to transfer to and install on new systems.
|
||||
Any program or application that uses Reticulum will automatically load and
|
||||
initialise Reticulum when it starts.
|
||||
|
||||
In many cases, this approach is sufficient. When any program needs to use
|
||||
Reticulum, it is loaded, initialised, interfaces are brought up, and the
|
||||
program can now communicate over Reticulum. If another program starts up
|
||||
and also wants access to the same Reticulum network, the instance is simply
|
||||
shared. This works for any number of programs running concurrently, and is
|
||||
very easy to use, but depending on your use case, there are other options.
|
||||
program can now communicate over any Reticulum networks available. If another
|
||||
program starts up and also wants access to the same Reticulum network, the
|
||||
instance is simply shared. This works for any number of programs running
|
||||
concurrently, and is very easy to use, but depending on your use case, there
|
||||
are other options.
|
||||
|
||||
Included Utility Programs
|
||||
-------------------------
|
||||
@@ -72,33 +74,49 @@ interfaces, similar to the ``ifconfig`` program.
|
||||
|
||||
# Example output
|
||||
Shared Instance[37428]
|
||||
Status: Up
|
||||
Connected applications: 1
|
||||
RX: 1.13 KB
|
||||
TX: 1.07 KB
|
||||
Status : Up
|
||||
Serving : 1 program
|
||||
Rate : 1.00 Gbps
|
||||
Traffic : 83.13 KB↑
|
||||
86.10 KB↓
|
||||
|
||||
UDPInterface[Default UDP Interface/0.0.0.0:4242]
|
||||
Status: Up
|
||||
RX: 1.01 KB
|
||||
TX: 1.01 KB
|
||||
AutoInterface[Local]
|
||||
Status : Up
|
||||
Mode : Full
|
||||
Rate : 10.00 Mbps
|
||||
Peers : 1 reachable
|
||||
Traffic : 63.23 KB↑
|
||||
80.17 KB↓
|
||||
|
||||
TCPInterface[RNS Testnet Frankfurt/frankfurt.rns.unsigned.io:4965]
|
||||
Status: Up
|
||||
RX: 1.37 KB
|
||||
TX: 9.02 KB
|
||||
Status : Up
|
||||
Mode : Full
|
||||
Rate : 10.00 Mbps
|
||||
Traffic : 187.27 KB↑
|
||||
74.17 KB↓
|
||||
|
||||
RNodeInterface[RNode UHF]
|
||||
Status : Up
|
||||
Mode : Access Point
|
||||
Rate : 1.30 kbps
|
||||
Access : 64-bit IFAC by <…e702c42ba8>
|
||||
Traffic : 8.49 KB↑
|
||||
9.23 KB↓
|
||||
|
||||
Reticulum Transport Instance <5245a8efe1788c6a70e1> running
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnsd [-h] [--config CONFIG] [-v] [-q] [--version]
|
||||
usage: rnstatus [-h] [--config CONFIG] [--version] [-a] [-v]
|
||||
|
||||
Reticulum Network Stack Daemon
|
||||
Reticulum Network Stack Status
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
-v, --verbose
|
||||
-q, --quiet
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-v, --verbose
|
||||
|
||||
|
||||
The rnpath Utility
|
||||
@@ -117,7 +135,8 @@ destinations on the Reticulum network.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnpath.py [-h] [--config CONFIG] [--version] [-v] [destination]
|
||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-d] [-w seconds] [-v]
|
||||
[destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
@@ -128,6 +147,9 @@ destinations on the Reticulum network.
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-d, --drop remove the path to a destination
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
|
||||
|
||||
@@ -177,8 +199,8 @@ These changes will be detailed here.
|
||||
Fixed Serial Port Names
|
||||
=======================
|
||||
|
||||
On a Reticulum node with several serial port based interfaces, it can be
|
||||
beneficial to use the fixed name device nodes for the serial ports, instead
|
||||
On a Reticulum instance with several serial port based interfaces, it can be
|
||||
beneficial to use the fixed device names for the serial ports, instead
|
||||
of the dynamically allocated shorthands such as ``/dev/ttyUSB0``. Under most
|
||||
Debian-based distributions, including Ubuntu and Raspberry Pi OS, these nodes
|
||||
can be found under ``/dev/serial/by-id``.
|
||||
@@ -203,7 +225,7 @@ Here is an example of a packet radio TNC configured as such:
|
||||
slottime = 20
|
||||
|
||||
Using this methodology avoids potential naming mix-ups where physical devices
|
||||
might be plugged and unplugged in different orders, or when node name
|
||||
might be plugged and unplugged in different orders, or when device name
|
||||
assignment varies from one boot to another.
|
||||
|
||||
.. _using-systemd:
|
||||
|
||||
+36
-14
@@ -2,9 +2,9 @@
|
||||
What is Reticulum?
|
||||
******************
|
||||
|
||||
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, that can operate even with very high latency and extremely low bandwidth.
|
||||
Reticulum is a cryptography-based networking stack for building wide-area networks with readily available hardware, that can continue to operate even with extremely low bandwidth and very high latency.
|
||||
|
||||
Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
|
||||
Reticulum allows you to build wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
|
||||
|
||||
Reticulum is a complete networking stack, and does not need IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
|
||||
|
||||
@@ -13,7 +13,7 @@ No kernel modules or drivers are required. Reticulum runs completely in userland
|
||||
|
||||
Current Status
|
||||
==============
|
||||
Reticulum should currently be considered beta software. All core protocol 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.
|
||||
Reticulum should currently be considered beta software. All core protocol 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 stable at the moment, but could change if absolutely warranted.
|
||||
|
||||
|
||||
What does Reticulum Offer?
|
||||
@@ -24,9 +24,9 @@ What does Reticulum Offer?
|
||||
|
||||
* Complete initiator anonymity, communicate without revealing your identity
|
||||
|
||||
* Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
||||
* Asymmetric encryption based on X25519, and Ed25519 signatures as a basis for all communication
|
||||
|
||||
* Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||
* Forward Secrecy by using ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||
|
||||
* Reticulum uses the `Fernet <https://github.com/fernet/spec/blob/master/Spec.md>`_ specification for on-the-wire / over-the-air encryption
|
||||
|
||||
@@ -44,6 +44,12 @@ What does Reticulum Offer?
|
||||
|
||||
* An intuitive and developer-friendly API
|
||||
|
||||
* Efficient link establishment
|
||||
|
||||
* Total bandwidth cost of setting up a link is only 3 packets, totalling 237 bytes
|
||||
|
||||
* Low cost of keeping links open at only 0.62 bits per second
|
||||
|
||||
* Reliable and efficient transfer of arbritrary amounts of data
|
||||
|
||||
* Reticulum can handle a few bytes of data or files of many gigabytes
|
||||
@@ -52,11 +58,9 @@ What does Reticulum Offer?
|
||||
|
||||
* The API is very easy to use, and provides transfer progress
|
||||
|
||||
* Efficient link establishment
|
||||
* Authentication and virtual network segmentation on all supported interface types
|
||||
|
||||
* Total bandwidth cost of setting up a link is only 3 packets, totalling 237 bytes
|
||||
|
||||
* Low cost of keeping links open at only 0.62 bits per second
|
||||
* Flexible scalability allowing extremely low-bandwidth networks to co-exist and interoperate with large, high-bandwidth networks
|
||||
|
||||
|
||||
Where can Reticulum be Used?
|
||||
@@ -68,9 +72,9 @@ ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||
of the types of interfaces Reticulum was designed for.
|
||||
|
||||
An open-source LoRa-based interface called `RNode <https://unsigned.io/rnode>`_
|
||||
has been designed specifically for use with Reticulum. It is possible to build
|
||||
yourself, or it can be purchased as a complete transceiver that just needs a
|
||||
USB connection to the host.
|
||||
has been designed as an example transceiver that is very suitable for
|
||||
Reticulum. It is possible to build it yourself, to transform a common LoRa
|
||||
development board into one, or it can be purchased as a complete transceiver.
|
||||
|
||||
Reticulum can also be encapsulated over existing IP networks, so there's
|
||||
nothing stopping you from using it over wired ethernet or your local WiFi
|
||||
@@ -80,22 +84,40 @@ self-configuring, resilient and encrypted mesh.
|
||||
|
||||
As an example, it's possible to set up a Raspberry Pi connected to both a
|
||||
LoRa radio, a packet radio TNC and a WiFi network. Once the interfaces are
|
||||
configured, Reticulum will take care of the rest, and any device on the WiFi
|
||||
added, Reticulum will take care of the rest, and any device on the WiFi
|
||||
network can communicate with nodes on the LoRa and packet radio sides of the
|
||||
network, and vice versa.
|
||||
|
||||
Interface Types and Devices
|
||||
===========================
|
||||
Reticulum implements a range of generalised interface types that covers most of the communications hardware that Reticulum can run over. If your hardware is not supported, it's relatively simple to implement an interface class. Currently, the following interfaces are supported:
|
||||
Reticulum implements a range of generalised interface types that covers the communications hardware that Reticulum can run over. If your hardware is not supported, it's relatively simple to implement an interface class. Currently, Reticulum can use the following devices and communication mediums:
|
||||
|
||||
* Any ethernet device
|
||||
|
||||
* WiFi devices
|
||||
|
||||
* Wired ethernet devices
|
||||
|
||||
* Fibre-optic transceivers
|
||||
|
||||
* Data radios with ethernet ports
|
||||
|
||||
* LoRa using `RNode <https://unsigned.io/rnode>`_
|
||||
|
||||
* Can be installed on `many popular LoRa boards <https://github.com/markqvist/rnodeconfigutil#supported-devices>`_
|
||||
|
||||
* Can be purchased as a `ready to use transceiver <https://unsigned.io/rnode>`_
|
||||
|
||||
* Packet Radio TNCs, such as `OpenModem <https://unsigned.io/openmodem>`_
|
||||
|
||||
* Any packet radio TNC in KISS mode
|
||||
|
||||
* Ideal for VHF and UHF radio
|
||||
|
||||
* Any device with a serial port
|
||||
|
||||
* The I2P network
|
||||
|
||||
* TCP over IP networks
|
||||
|
||||
* UDP over IP networks
|
||||
|
||||
Reference in New Issue
Block a user