Compare commits

...

117 Commits

Author SHA1 Message Date
Mark Qvist 7221becd35 Updated manual 2025-01-19 21:09:50 +01:00
Mark Qvist a51f5f2eaf Updated changelog 2025-01-19 21:09:40 +01:00
Mark Qvist 9e8d71ddaf Updated examples 2025-01-19 20:38:41 +01:00
Mark Qvist 9bc55a9047 Updated manual 2025-01-19 12:56:32 +01:00
Mark Qvist 3e7ab5136e Better thread configuration 2025-01-19 00:57:36 +01:00
Mark Qvist d2cf3c2a7e Added error on configured radio parameter mismatch on Android 2025-01-19 00:10:35 +01:00
Mark Qvist 77519f1a0c Updated version 2025-01-18 21:38:13 +01:00
Mark Qvist e869b3cac9 Added resource reject signalling 2025-01-18 21:37:17 +01:00
Mark Qvist a2878f1722 Cleanup 2025-01-17 12:48:10 +01:00
Mark Qvist 748a7290a9 Updated docs 2025-01-17 12:47:02 +01:00
Mark Qvist 6e80a553c8 Updated changelog 2025-01-17 12:46:54 +01:00
Mark Qvist ec7aa44a17 Updated version 2025-01-17 12:41:48 +01:00
Mark Qvist 4fa335639c Added T3S3 support to rnodeconf 2025-01-16 19:10:49 +01:00
Mark Qvist 67195c0b14 Improved logging 2025-01-16 17:49:50 +01:00
Mark Qvist ad1e6a41ee Improved daemon restart time on systems with many interfaces 2025-01-16 17:48:16 +01:00
Mark Qvist a56d93fc1e Fixed potential logging deadlock 2025-01-16 17:37:47 +01:00
Mark Qvist b8aa6a3e44 Improved LocalInterface detach 2025-01-16 15:57:43 +01:00
Mark Qvist 1709cd929a Improved interface detach on shared instance shutdown 2025-01-16 14:12:30 +01:00
Mark Qvist 4f4961257c Improved interface detach on shared instance shutdown 2025-01-16 14:09:18 +01:00
Mark Qvist 1b48f43a0d Corrected T3S3 SX1280 model codes in rnodeconf 2025-01-16 12:04:54 +01:00
Mark Qvist e5d446a54e Retry ratchet reload on potential I/O conflicts 2025-01-16 12:04:29 +01:00
Mark Qvist 0af768e742 Cleanup 2025-01-14 19:36:49 +01:00
Mark Qvist 1a7d20a8d6 Cleanup 2025-01-14 19:02:15 +01:00
Mark Qvist ec4f4d5a83 Cleanup 2025-01-14 18:57:02 +01:00
Mark Qvist 8cefa4b2a9 Improved resource transfer timing 2025-01-14 18:24:56 +01:00
Mark Qvist 2331f1ea3e Fixed link MTU clamping 2025-01-14 18:21:31 +01:00
Mark Qvist be7dafa30c Added MTU autoconfiguration on interfaces 2025-01-14 18:19:51 +01:00
Mark Qvist 3e20cb1b67 Added resource EIFR continuity to split resource handling 2025-01-14 18:19:07 +01:00
Mark Qvist 097e136662 Fixed rnstatus display bug 2025-01-14 18:18:27 +01:00
Mark Qvist e3a716224d Implemented MTU autoconfiguration on interfaces 2025-01-14 18:17:53 +01:00
Mark Qvist 80dc567a53 Handle negative time in time formatters 2025-01-14 12:45:17 +01:00
Mark Qvist c6576d6504 Added link MTU discovery configuration option 2025-01-14 00:13:56 +01:00
Mark Qvist 89d5d9517d Added print device config option to rnodeconf 2025-01-13 21:48:35 +01:00
Mark Qvist dc315653c0 Added interference status to RNodeInterface 2025-01-13 21:06:24 +01:00
Mark Qvist 746b403890 Noise floor output formatting 2025-01-13 16:37:18 +01:00
Mark Qvist fc619460f0 Updated manual 2025-01-13 15:42:46 +01:00
Mark Qvist cd0f82d9ad Updated tests 2025-01-13 15:42:32 +01:00
Mark Qvist 330c2aacac Fixed incorrect resource SDU calculation when link MTU is set 2025-01-13 14:42:03 +01:00
Mark Qvist 63da084bbe Updated docs 2025-01-13 14:41:38 +01:00
Mark Qvist cbbd8221ee Fixed typo 2025-01-13 14:41:21 +01:00
Mark Qvist 1d18d53052 Updated speedtest example 2025-01-12 23:48:56 +01:00
Mark Qvist ceccf3153b Correct link MDU calculation 2025-01-12 23:48:21 +01:00
Mark Qvist bde33e7d84 Added support for dynamic link MTU to Channel and Buffer 2025-01-12 23:26:18 +01:00
Mark Qvist 93330d96a0 Updated manual 2025-01-12 20:56:13 +01:00
Mark Qvist d93ce62878 Updated HW MTUs 2025-01-12 20:56:06 +01:00
Mark Qvist eafa4aefbb Added log format 2025-01-12 18:51:27 +01:00
Mark Qvist 53df2fa5e0 Improved profiler 2025-01-12 17:51:02 +01:00
Mark Qvist abc657806d Added cumulative utilisation to profiler 2025-01-12 17:32:11 +01:00
Mark Qvist a0f219f7f4 Last-hop LR MTU clamping 2025-01-12 17:31:17 +01:00
Mark Qvist 47eba03a4b Single HW_MTU field 2025-01-12 17:29:06 +01:00
Mark Qvist 3289cd1299 Cleanup 2025-01-12 17:28:32 +01:00
Mark Qvist ab5fcd7a5b Added live traffic stats counting and output to rnstatus 2025-01-11 19:30:00 +01:00
Mark Qvist 45494f21aa Allow IFAC bitmask generation for large packet sizes 2025-01-11 17:26:51 +01:00
Mark Qvist 5d677d2fb7 Set correct hardware MTU 2025-01-11 17:25:03 +01:00
Mark Qvist 808082e300 Link proof MTU 2025-01-11 17:24:39 +01:00
Mark Qvist 97cfdfd023 Unify link ID across versions regardless of MTU discovery support 2025-01-11 16:58:09 +01:00
Mark Qvist 9b15cf2295 Check link MTU discovery is enabled 2025-01-11 15:52:40 +01:00
Mark Qvist eaa68c2d04 Updated docs 2025-01-11 14:56:45 +01:00
Mark Qvist ac5ca78c77 Improved split resource transfer performance 2025-01-11 14:25:27 +01:00
Mark Qvist 5b17dbdfd6 Improved packet filter performance 2025-01-11 14:24:40 +01:00
Mark Qvist d4ed20c7d5 Improved rncp status output 2025-01-11 14:23:53 +01:00
Mark Qvist a5093ea8f0 Updated version 2025-01-11 13:22:49 +01:00
Mark Qvist f5cf438abd Improve resource transfer throughput on high-MTU links 2025-01-11 13:22:18 +01:00
Mark Qvist bf6e73e163 Path MTU discovery for links 2025-01-11 11:43:47 +01:00
Mark Qvist 503f475ca5 Read link MTU from link request packet 2025-01-11 03:12:31 +01:00
Mark Qvist 8506118aee Cleanup 2025-01-11 01:45:09 +01:00
Mark Qvist dfa295a90a Cleanup 2025-01-11 01:31:57 +01:00
Mark Qvist 3ace1583da Packets go brrrr 2025-01-11 01:26:46 +01:00
Mark Qvist c62b66195d Optimised profiler timing overhead 2025-01-10 21:37:45 +01:00
Mark Qvist b724836d2b Changed profiler to context manager 2025-01-10 20:07:17 +01:00
Mark Qvist 1e1b9dc79e Fixed missing check for dict entry existence 2025-01-10 12:40:11 +01:00
Mark Qvist c668a51e39 Cleanup 2025-01-10 12:39:25 +01:00
Mark Qvist 09b34d34c6 Only persist ratchets when new 2025-01-10 12:39:04 +01:00
Mark Qvist 54e18e41c5 Updated changelog 2025-01-10 11:42:30 +01:00
Mark Qvist 5550bca040 Updated manual 2025-01-10 11:42:06 +01:00
Mark Qvist f7a02351d4 Added interference avoidance configuration to rnodeconf 2025-01-09 17:46:12 +01:00
Mark Qvist 3125b99043 Cleanup 2025-01-09 15:21:59 +01:00
Mark Qvist 158765abb7 Added noise floor stat output to rnodeconf 2025-01-09 15:18:29 +01:00
Mark Qvist 81aa9ac5b6 Added Heltec T114 to rnodeconf 2025-01-09 15:17:41 +01:00
Mark Qvist 55f5842587 Added new channel stat and CSMA parameters to RNodeInterface 2025-01-09 15:15:54 +01:00
Mark Qvist 38dd63a99a Updated issue template 2025-01-06 11:38:37 +01:00
Mark Qvist 558cd6c4a7 Updated version 2025-01-06 11:38:29 +01:00
Mark Qvist 15e6a1bfde Add support for SX1280 with PA 2025-01-03 22:35:01 +01:00
Mark Qvist c1087e62fd Added ability to initiate display reconditioning to rnodeconf 2024-12-31 14:14:14 +01:00
Mark Qvist 9d924dcd6d Added ability to set display rotation to rnodeconf 2024-12-31 13:22:57 +01:00
Mark Qvist 163d2ed157 Fixed missing console image install on Heltec V3 2024-12-12 13:06:52 +01:00
Mark Qvist 68f07ddd38 Updated manual 2024-12-11 22:26:48 +01:00
Mark Qvist d956b93c13 Updated changelog 2024-12-11 22:26:41 +01:00
Mark Qvist 3036305662 Cleanup 2024-12-11 22:17:58 +01:00
Mark Qvist ee603ce68e Updated manual 2024-12-11 19:56:37 +01:00
Mark Qvist 989513cb46 Updated version 2024-12-11 19:41:35 +01:00
Mark Qvist 7e52c37580 Allow announce handler to receive announce packet hash 2024-12-11 19:18:02 +01:00
Mark Qvist 0984f92fa2 Fixed typo 2024-12-11 19:17:14 +01:00
Mark Qvist 2ab2d8e9df Updated changelog 2024-12-09 22:22:22 +01:00
Mark Qvist b828e0e858 Updated manual 2024-12-09 22:10:46 +01:00
Mark Qvist d4dd706bba Merge branch 'master' of github.com:markqvist/Reticulum 2024-12-08 14:27:37 +01:00
Mark Qvist ed30fa3e0a Added ability to reflect RNS logs to app-internal log handler callback 2024-12-08 14:27:17 +01:00
Mark Qvist 5e2b3df623 Added ability to run rnstatus as application-local imported module 2024-12-08 14:26:51 +01:00
Mark Qvist ae7dffdfc0 Added display read command to RNodeInterface 2024-12-08 14:25:58 +01:00
Mark Qvist 32b5c7a3af Updated documentation 2024-12-08 14:24:51 +01:00
markqvist 8b08658b7f Merge pull request #629 from jacobeva/refactor-fix
Fix RNodeMultiInterface to work with refactored interfaces
2024-12-07 22:32:27 +01:00
jacob.eva ee79c3a732 Fix RNodeMultiInterface to work with refactored interfaces 2024-12-07 21:28:14 +00:00
Mark Qvist 0e5f4aa08a Fixed missing artifact 2024-12-05 16:43:58 +01:00
Mark Qvist ec0407e5c8 Updated version 2024-12-05 16:40:53 +01:00
Mark Qvist db1380c413 Disable building manual 2024-12-05 16:36:44 +01:00
markqvist 7e3979dac0 Merge pull request #626 from gretel/add-revised-workflow
ci/cd: add release automation
2024-12-05 16:29:24 +01:00
Mark Qvist c1b6bde4a7 Updated documentation 2024-12-02 14:24:42 +01:00
Mark Qvist 8df89cc2d0 Allow dynamic sub-module import from compiled python bytecode 2024-12-02 14:20:34 +01:00
Mark Qvist 19adadf4cf Fixed imports for OpenWRT build 2024-12-01 09:09:39 +01:00
gretel c30feb3fc2 ci/cd: add release automation
Publishes a release when tagged with a `semver` version:
- X.Y.Z for "production quality" (1.0.0)
- X.Y.Z-suffix for development (1.0.0-alpha.1)

Release will be marked as 'prerelease' accordingly.

For now, any release will be marked 'draft'.
2024-11-30 21:43:54 +01:00
Mark Qvist 4c81589d5b Updated manual 2024-11-30 01:08:58 +01:00
Mark Qvist c014357e24 Updated documentation 2024-11-29 15:11:51 +01:00
Mark Qvist ec41dc1a03 Updated documentation 2024-11-29 15:11:47 +01:00
Mark Qvist 463dfa6fb4 Updated documentation 2024-11-29 15:10:35 +01:00
Mark Qvist 0354b5969d Updated documentation 2024-11-29 10:12:44 +01:00
Mark Qvist fc225bd55d Updated getting started and install instructions sections 2024-11-29 10:12:34 +01:00
Mark Qvist 67562126fc Refactored interface imports 2024-11-27 17:45:05 +01:00
84 changed files with 2699 additions and 877 deletions
+6 -2
View File
@@ -15,7 +15,11 @@ Before creating a bug report on this issue tracker, you **must** read the [Contr
- After reading the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md), delete this section from your bug report.
**Describe the Bug**
A clear and concise description of what the bug is.
First of all: Is this really a bug? Is it reproducible?
If this is a request for help because something is not working as you expected, stop right here, and go to the [discussions](https://github.com/markqvist/Reticulum/discussions) instead, where you can post your questions and get help from other users.
If this really is a bug or issue with the software, remove this section of the template, and provide **a clear and concise description of what the bug is**.
**To Reproduce**
Describe in detail how to reproduce the bug.
@@ -24,7 +28,7 @@ Describe in detail how to reproduce the bug.
A clear and concise description of what you expected to happen.
**Logs & Screenshots**
Please include any relevant log output. If applicable, also add screenshots to help explain your problem.
Please include any relevant log output. If applicable, also add screenshots to help explain your problem. In most cases, without any relevant log output, we will not be able to determine the cause of the bug, or reproduce it.
**System Information**
- OS and version
+96
View File
@@ -0,0 +1,96 @@
name: Build Reticulum
on:
push:
branches:
- '*'
tags:
- "[0-9]+.[0-9]+.[0-9]+*"
pull_request:
branches:
- master
paths-ignore:
- .gitignore
- LICENSE
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- run: make test
package:
needs: test
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
environment: ${{ contains(github.ref, '-') && 'development' || 'production' }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- run: |
python -m pip install -q build wheel setuptools
make remove_symlinks
make build_wheel
make build_pure_wheel
make create_symlinks
- uses: actions/upload-artifact@v4
with:
name: package
path: dist/*.whl
# documentation:
# needs: test
# if: startsWith(github.ref, 'refs/tags/')
# runs-on: ubuntu-latest
# environment: ${{ contains(github.ref, '-') && 'development' || 'production' }}
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-python@v5
# with:
# python-version: 3.x
# - run: |
# sudo apt-get -qq update && sudo apt-get -qq install latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended
# python -m pip -q install sphinx sphinx-copybutton
# cd docs && make latexpdf && make epub
# - uses: actions/upload-artifact@v4
# with:
# name: documentation
# path: |
# docs/build/latex/*.pdf
# docs/build/epub/*.epub
release:
needs: [package]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
environment: ${{ contains(github.ref, '-') && 'development' || 'production' }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: .artifacts
- uses: softprops/action-gh-release@v2
with:
files: |
# .artifacts/package/**.whl
# .artifacts/documentation/latex/reticulumnetworkstack.pdf
# .artifacts/documentation/epub/ReticulumNetworkStack.epub
draft: true
generate_release_notes: true
prerelease: ${{ contains(github.ref, '-') }}
fail_on_unmatched_files: true
-28
View File
@@ -1,28 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Test suite
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Test
run: |
make test
+107
View File
@@ -1,3 +1,110 @@
### 2025-01-19: RNS β 0.9.1
This maintenance release adds reject signalling mechanism to resource transfers, fixes inconsistencies in the code examples, and improves thread configuration in the transport core.
**Changes**
- Added resource reject signalling
- Added error reporting on configured radio parameter mismatch on Android
- Improved thread configuration for transport core threads
- Updated examples
**Release Hashes**
```
49288a562ad6d4b5647c3afec051a6bb6497b75e3f165a972436134d4a93ad76 rns-0.9.1-py3-none-any.whl
abd6c4bdead2fc25d0b9b2cda5708586e8cb776b088f2a901a5f262e2ed901ae rnspure-0.9.1-py3-none-any.whl
```
### 2025-01-17: RNS β 0.9.0
This release lays the groundwork for future performance and resource utilisation optimisations. Most importantly, this release adds **link MTU autodiscovery**, which allow established links to use much higher MTUs than the base MTU of 500 bytes.
**Please note!** To actually use link MTU discovery, all transport nodes along the path must be upgraded to at least version `0.9.0`. Since this is the first release to add support for this feature, *it is currently **not** activated by default*, and no clients or applications will use it yet. Using link MTU autodiscovery by default will be enabled by default in RNS version `0.9.1`. Please upgrade your nodes!
Additionally, this release adds several new features, performance improvements and bug fixes, as well as support for RNodes running firmware version `1.81`.
**Changes**
- Added MTU autoconfiguration on interfaces that support higher MTUs
- Added link MTU autodiscovery and path clamping
- Added dynamic SDU calculations based on link MTU to `Resource`, `Channel` and `Buffer`
- Added resource EIFR continuity to split resource handling
- Added interference status to `RNodeInterface`
- Fixed a display bug in `rnstatus`
- Added live traffic stats to `rnstatus`
- Added T3S3 support to `rnodeconf`
- Added Heltec T114 support to `rnodeconf`
- Added LilyGO T-Echo support to `rnodeconf`
- Added option to print device configuration to `rnodeconf`
- Improved CPU utilisation and memory consumption
- Improved `rnsd` restart time on systems with many interfaces
- Improved `rncp` status output
- Improved packet filter performance
- Improved interface detachment handling
- Improved resource transfer timing and performance
- Improved Transport core efficiency
- Improved reliability of ratchet reloads if I/O conflicts occur
- Improved logging
- Improved built-in profiler
- Fixed a potential deadlock in logging
- Fixed time formatters not handling negative times
- Updated example code
**Release Hashes**
```
1ee60634cf0627c45b93f4e6c9adaf1fcdf9c1a8dfd4dd3dcd499e029554ab4f rns-0.9.0-py3-none-any.whl
b67eec583fdb224ba8174b317e66b8f7344e338e93760ed1a90f0bafea8cf09e rnspure-0.9.0-py3-none-any.whl
```
### 2025-01-09: RNS β 0.8.9
This maintenance release adds a number of configuration options to `rnodeconf`.
**Changes**
- Added noise floor output to `rnstatus` for supported interfaces
- Added channel noise floor and CSMA parameter reporting to `RNodeInterface`
- Added ability to set display rotation in `rnodeconf`
- Added ability to configure interference avoidance to `rnodeconf`
- Fixed missing console image install on Heltec V3 in `rnodeconf`
**Release Hashes**
```
b54fe8bc296f83a3a70569c9d1e9db3096249789c18f8d0217671479fa6881a1 rns-0.8.9-py3-none-any.whl
52fd992e5f9478d5a1f61f8f37dc0ee2d268fdd0b8a4e6656d33d632490afc5a rnspure-0.8.9-py3-none-any.whl
```
### 2024-12-11: RNS β 0.8.8
This maintenance release adds a single API function and fixes a bug.
**Changes**
- Allow announce handlers to receive announce packet hash
- Fix packet RSSI/SNR/Q cache not being available on standalone instances
**Release Hashes**
```
9c1755a81049c67b051ecb9fe4b2c5f7d98bf09d20ed52d6ce6a410298b0527b rns-0.8.8-py3-none-any.whl
d8871d69cde4b0a0b99b383f324d651dc77a2f44ec9641be828902c778a8d128 rnspure-0.8.8-py3-none-any.whl
```
### 2024-12-09: RNS β 0.8.7
This maintenance release adds support for OpenWRT packaging, and brings several minor improvements and bugfixes.
Thanks to @gretel and @jacobeva, who contributed to this release!
**Changes**
- Added support for packaging RNS to OpenWRT
- Added ability to run `rnstatus` as application-local imported module
- Added ability to reflect RNS log output to app-internal log handler callback
- Added display read functionality to `RNodeInterface`
- Fixed a regression in `RNodeMultiInterface` caused by earlier refactoring
- Imrpoved documentation
**Release Hashes**
```
e76ba8feeeae2c8df27e9906deebd7c721f0f0e887ad3fbd26df0212d6ce907a rns-0.8.7-py3-none-any.whl
046608539bc235d52c970c7f3c54e7aa01a86016ae00263f8a55fc796b6939f5 rnspure-0.8.7-py3-none-any.whl
```
### 2024-11-24: RNS β 0.8.6
This release adds full interface modularity and custom interface loading to RNS. Users can now easily create and use their own custom interfaces for communicating over practically anything. Support for IPv6 has also been added to the TCP-based interfaces.
+2 -1
View File
@@ -6,6 +6,7 @@
import argparse
import random
import sys
import RNS
# Let's define an app name. We'll use this for all
@@ -168,4 +169,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+1 -1
View File
@@ -118,4 +118,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+3 -4
View File
@@ -157,7 +157,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -254,9 +254,8 @@ def link_closed(link):
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
sys.exit(0)
# When the buffer has new data, read it and write it to the terminal.
def client_buffer_ready(ready_bytes: int):
@@ -320,4 +319,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+5 -6
View File
@@ -124,7 +124,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link example "+
"Channel example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
)
@@ -212,7 +212,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -276,7 +276,7 @@ def client_loop():
packed_size = len(message.pack())
channel = server_link.get_channel()
if channel.is_ready_to_send():
if packed_size <= channel.MDU:
if packed_size <= channel.mdu:
channel.send(message)
else:
RNS.log(
@@ -321,9 +321,8 @@ def link_closed(link):
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
sys.exit(0)
# When a packet is received over the channel, we
# simply print out the data.
@@ -387,4 +386,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+3 -2
View File
@@ -6,6 +6,7 @@
##########################################################
import argparse
import sys
import RNS
# Let's define an app name. We'll use this for all
@@ -130,7 +131,7 @@ def client(destination_hexhash, configpath, timeout=None):
except Exception as e:
RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -328,4 +329,4 @@ if __name__ == "__main__":
client(args.destination, configarg, timeout=timeoutarg)
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+4 -5
View File
@@ -224,7 +224,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -462,7 +462,7 @@ def filelist_timeout_job():
global server_files
if len(server_files) == 0:
RNS.log("Timed out waiting for filelist, exiting")
os._exit(0)
sys.exit(0)
# When a link is closed, we'll inform the
@@ -475,9 +475,8 @@ def link_closed(link):
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
sys.exit(0)
# When RNS detects that the download has
# started, we'll update our menu state
@@ -601,4 +600,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+3 -4
View File
@@ -133,7 +133,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -245,9 +245,8 @@ def link_closed(link):
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
sys.exit(0)
# When a packet is received over the link, we
# simply print out the data.
@@ -311,4 +310,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+3 -4
View File
@@ -119,7 +119,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -222,9 +222,8 @@ def link_closed(link):
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
sys.exit(0)
# When a packet is received over the link, we
# simply print out the data.
@@ -288,4 +287,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+2 -1
View File
@@ -5,6 +5,7 @@
##########################################################
import argparse
import sys
import RNS
# Let's define an app name. We'll use this for all
@@ -98,4 +99,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+3 -5
View File
@@ -5,6 +5,7 @@
##########################################################
import argparse
import sys
import RNS
# Let's define an app name. We'll use this for all
@@ -25,9 +26,6 @@ def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# TODO: Remove
RNS.loglevel = RNS.LOG_DEBUG
# Randomly create a new identity for our echo server
server_identity = RNS.Identity()
@@ -141,7 +139,7 @@ def client(destination_hexhash, configpath, timeout=None):
except Exception as e:
RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -340,4 +338,4 @@ if __name__ == "__main__":
client(args.destination, configarg, timeout=timeoutarg)
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+3 -4
View File
@@ -119,7 +119,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -226,9 +226,8 @@ def link_closed(link):
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
sys.exit(0)
##########################################################
@@ -284,4 +283,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+10 -20
View File
@@ -149,8 +149,6 @@ def server_packet_received(message, packet):
time.sleep(0.2)
rc = 0
received_data = 0
# latest_client_link.teardown()
# os._exit(0)
##########################################################
@@ -159,6 +157,7 @@ def server_packet_received(message, packet):
# A reference to the server link
server_link = None
should_quit = False
# This initialisation is executed when the users chooses
# to run as a client
@@ -175,7 +174,7 @@ def client(destination_hexhash, configpath):
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
sys.exit(0)
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
@@ -216,7 +215,7 @@ def client(destination_hexhash, configpath):
client_loop()
def client_loop():
global server_link
global server_link, should_quit
# Wait for the link to become active
while not server_link:
@@ -224,16 +223,7 @@ def client_loop():
should_quit = False
while not should_quit:
try:
text = input()
# Check if we should quit the example
if text == "quit" or text == "q" or text == "exit":
should_quit = True
server_link.teardown()
except Exception as e:
raise e
time.sleep(0.2)
# This function is called when a link
# has been established with the server
@@ -246,8 +236,8 @@ def link_established(link):
# Inform the user that the server is
# connected
RNS.log("Link established with server,sending...")
rd = os.urandom(RNS.Link.MDU)
RNS.log("Link established with server, sending...")
rd = os.urandom(link.mdu)
started = time.time()
while link.status == RNS.Link.ACTIVE and data_sent < data_cap*1.25:
RNS.Packet(server_link, rd, create_receipt=False).send()
@@ -276,17 +266,17 @@ def link_established(link):
# When a link is closed, we'll inform the
# user, and exit the program
def link_closed(link):
global should_quit
if link.teardown_reason == RNS.Link.TIMEOUT:
RNS.log("The link timed out, exiting now")
elif link.teardown_reason == RNS.Link.DESTINATION_CLOSED:
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
should_quit = True
time.sleep(1.5)
os._exit(0)
sys.exit(0)
def client_packet_received(message, packet):
pass
@@ -344,4 +334,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
sys.exit(0)
+1 -1
View File
@@ -1,4 +1,4 @@
Reticulum Network Stack β <img align="right" src="https://static.pepy.tech/personalized-badge/rns?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Installs" style="padding-left:10px"/><a href="https://github.com/markqvist/reticulum/actions/workflows/python-app.yml"><img align="right" src="https://github.com/markqvist/reticulum/actions/workflows/python-app.yml/badge.svg"/></a>
Reticulum Network Stack β <img align="right" src="https://static.pepy.tech/personalized-badge/rns?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Installs" style="padding-left:10px"/><a href="https://github.com/markqvist/Reticulum/actions/workflows/build.yml"><img align="right" src="https://github.com/markqvist/Reticulum/actions/workflows/build.yml/badge.svg"/></a>
==========
<p align="center"><img width="200" src="https://raw.githubusercontent.com/markqvist/Reticulum/master/docs/source/graphics/rns_logo_512.png"></p>
+3 -1
View File
@@ -45,7 +45,8 @@ class StreamDataMessage(MessageBase):
The stream id is limited to 2 bytes - 2 bit
"""
MAX_DATA_LEN = RNS.Link.MDU - 2 - 6 # 2 for stream data message header, 6 for channel envelope
OVERHEAD = 2 + 6 # 2 for stream data message header, 6 for channel envelope
MAX_DATA_LEN = RNS.Link.MDU - OVERHEAD
"""
When the Buffer package is imported, this value is
calculcated based on the value of OVERHEAD
@@ -215,6 +216,7 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
self._stream_id = stream_id
self._channel = channel
self._eof = False
self._mdu = channel.mdu - StreamDataMessage.OVERHEAD
def write(self, __b: bytes) -> int | None:
try:
+6 -3
View File
@@ -602,7 +602,7 @@ class Channel(contextlib.AbstractContextManager):
return envelope
@property
def MDU(self):
def mdu(self):
"""
Maximum Data Unit: the number of bytes available
for a message to consume in a single send. This
@@ -611,7 +611,10 @@ class Channel(contextlib.AbstractContextManager):
:return: number of bytes available
"""
return self._outlet.mdu - 6 # sizeof(msgtype) + sizeof(length) + sizeof(sequence)
mdu = self._outlet.mdu - 6 # sizeof(msgtype) + sizeof(length) + sizeof(sequence)
if mdu > 0xFFFF:
mdu = 0xFFFF
return mdu
class LinkChannelOutlet(ChannelOutletBase):
@@ -639,7 +642,7 @@ class LinkChannelOutlet(ChannelOutletBase):
@property
def mdu(self):
return self.link.MDU
return self.link.mdu
@property
def rtt(self):
+1 -1
View File
@@ -48,7 +48,7 @@ def hkdf(length=None, derive_from=None, salt=None, context=None):
derived = b""
for i in range(ceil(length / hash_len)):
block = hmac_sha256(pseudorandom_key, block + context + bytes([i + 1]))
block = hmac_sha256(pseudorandom_key, block + context + bytes([(i + 1)%(0xFF+1)]))
derived += block
return derived[:length]
+4 -2
View File
@@ -20,5 +20,7 @@ elif cp.PROVIDER == cp.PROVIDER_PYCA:
from RNS.Cryptography.Proxies import Ed25519PrivateKeyProxy as Ed25519PrivateKey
from RNS.Cryptography.Proxies import Ed25519PublicKeyProxy as Ed25519PublicKey
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
py_modules = glob.glob(os.path.dirname(__file__)+"/*.py")
pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
modules = py_modules+pyc_modules
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
+13 -1
View File
@@ -424,7 +424,7 @@ class Destination:
def _reload_ratchets(self, ratchets_path):
if os.path.isfile(ratchets_path):
with self.ratchet_file_lock:
try:
def load_attempt():
ratchets_file = open(ratchets_path, "rb")
persisted_data = umsgpack.unpackb(ratchets_file.read())
if "signature" in persisted_data and "ratchets" in persisted_data:
@@ -433,10 +433,22 @@ class Destination:
self.ratchets_path = ratchets_path
else:
raise KeyError("Invalid ratchet file signature")
try:
try:
load_attempt()
except Exception as e:
RNS.trace_exception(e)
RNS.log(f"First ratchet reload attempt for {self} failed. Possible I/O conflict. Retrying in 500ms.", RNS.LOG_ERROR)
time.sleep(0.5)
load_attempt()
RNS.log(f"Ratchet reload retry succeeded", RNS.LOG_DEBUG)
except Exception as e:
self.ratchets = None
self.ratchets_path = None
RNS.trace_exception(e)
raise OSError("Could not read ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
else:
RNS.log("No existing ratchet data found, initialising new ratchet file for "+str(self), RNS.LOG_DEBUG)
+23 -19
View File
@@ -266,30 +266,34 @@ class Identity:
@staticmethod
def _remember_ratchet(destination_hash, ratchet):
# TODO: Remove at some point, and only log new ratchets
RNS.log(f"Remembering ratchet {RNS.prettyhexrep(Identity._get_ratchet_id(ratchet))} for {RNS.prettyhexrep(destination_hash)}", RNS.LOG_EXTREME)
try:
Identity.known_ratchets[destination_hash] = ratchet
if destination_hash in Identity.known_ratchets and Identity.known_ratchets[destination_hash] == ratchet:
ratchet_exists = True
else:
ratchet_exists = False
if not RNS.Transport.owner.is_connected_to_shared_instance:
def persist_job():
with Identity.ratchet_persist_lock:
hexhash = RNS.hexrep(destination_hash, delimit=False)
ratchet_data = {"ratchet": ratchet, "received": time.time()}
if not ratchet_exists:
RNS.log(f"Remembering ratchet {RNS.prettyhexrep(Identity._get_ratchet_id(ratchet))} for {RNS.prettyhexrep(destination_hash)}", RNS.LOG_EXTREME)
Identity.known_ratchets[destination_hash] = ratchet
if not RNS.Transport.owner.is_connected_to_shared_instance:
def persist_job():
with Identity.ratchet_persist_lock:
hexhash = RNS.hexrep(destination_hash, delimit=False)
ratchet_data = {"ratchet": ratchet, "received": time.time()}
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
if not os.path.isdir(ratchetdir):
os.makedirs(ratchetdir)
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
if not os.path.isdir(ratchetdir):
os.makedirs(ratchetdir)
outpath = f"{ratchetdir}/{hexhash}.out"
finalpath = f"{ratchetdir}/{hexhash}"
with open(outpath, "wb") as ratchet_file:
ratchet_file.write(umsgpack.packb(ratchet_data))
os.replace(outpath, finalpath)
outpath = f"{ratchetdir}/{hexhash}.out"
finalpath = f"{ratchetdir}/{hexhash}"
with open(outpath, "wb") as ratchet_file:
ratchet_file.write(umsgpack.packb(ratchet_data))
os.replace(outpath, finalpath)
threading.Thread(target=persist_job, daemon=True).start()
threading.Thread(target=persist_job, daemon=True).start()
except Exception as e:
RNS.log(f"Could not persist ratchet for {RNS.prettyhexrep(destination_hash)} to storage.", RNS.LOG_ERROR)
+1 -1
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from time import sleep
import sys
import threading
+141 -18
View File
@@ -64,10 +64,12 @@ class KISS():
CMD_STAT_CHTM = 0x25
CMD_STAT_PHYPRM = 0x26
CMD_STAT_BAT = 0x27
CMD_STAT_CSMA = 0x28
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_FB_EXT = 0x41
CMD_FB_READ = 0x42
CMD_DISP_READ = 0x66
CMD_FB_WRITE = 0x43
CMD_BT_CTRL = 0x46
CMD_PLATFORM = 0x48
@@ -83,15 +85,16 @@ class KISS():
RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02
ERROR_EEPROM_LOCKED = 0x03
ERROR_QUEUE_FULL = 0x04
ERROR_MEMORY_LOW = 0x05
ERROR_MODEM_TIMEOUT = 0x06
CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02
ERROR_EEPROM_LOCKED = 0x03
ERROR_QUEUE_FULL = 0x04
ERROR_MEMORY_LOW = 0x05
ERROR_MODEM_TIMEOUT = 0x06
ERROR_INVALID_FIRMWARE = 0x10
ERROR_INVALID_BLE_MTU = 0x20
ERROR_INVALID_CONFIG = 0x40
PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80
@@ -241,7 +244,7 @@ class RNodeInterface(Interface):
DEFAULT_IFAC_SIZE = 8
FREQ_MIN = 137000000
FREQ_MAX = 1020000000
FREQ_MAX = 3000000000
RSSI_OFFSET = 157
@@ -262,6 +265,8 @@ class RNodeInterface(Interface):
BATTERY_STATE_CHARGING = 0x02
BATTERY_STATE_CHARGED = 0x03
DISPLAY_READ_INTERVAL = 1.0
@classmethod
def bluetooth_control(device_serial = None, port = None, enable_bluetooth = False, disable_bluetooth = False, pairing_mode = False):
if (port != None or device_serial != None) and (enable_bluetooth or disable_bluetooth or pairing_mode):
@@ -462,12 +467,29 @@ class RNodeInterface(Interface):
self.r_airtime_long = 0.0
self.r_channel_load_short = 0.0
self.r_channel_load_long = 0.0
self.r_symbol_time_ms = None
self.r_symbol_rate = None
self.r_preamble_symbols = None
self.r_premable_time_ms = None
self.r_symbol_time_ms = None
self.r_symbol_rate = None
self.r_preamble_symbols = None
self.r_premable_time_ms = None
self.r_csma_slot_time_ms = None
self.r_csma_difs_ms = None
self.r_csma_cw_band = None
self.r_csma_cw_min = None
self.r_csma_cw_max = None
self.r_current_rssi = None
self.r_noise_floor = None
self.r_battery_state = RNodeInterface.BATTERY_STATE_UNKNOWN
self.r_battery_percent = 0
self.r_framebuffer = b""
self.r_framebuffer_readtime = 0
self.r_framebuffer_latency = 0
self.r_disp = b""
self.r_disp_readtime = 0
self.r_disp_latency = 0
self.should_read_display = False
self.read_display_interval = RNodeInterface.DISPLAY_READ_INTERVAL
self.packet_queue = []
self.flow_control = flow_control
@@ -703,6 +725,7 @@ class RNodeInterface(Interface):
RNS.log("After configuring "+str(self)+", the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
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.hw_errors.append({"error": KISS.ERROR_INVALID_CONFIG, "description": "The configuration parameters were not validated by the device. Make sure that the device actually supports the TX power, frequency, bandwidth, spreading factor and coding rate you configured."})
if self.serial != None:
self.serial.close()
@@ -809,6 +832,33 @@ class RNodeInterface(Interface):
if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device")
def read_framebuffer(self):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_READ])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
self.r_framebuffer_readtime = time.time()
if written != len(kiss_command):
raise IOError("An IO error occurred while sending framebuffer read command")
def read_display(self):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_READ])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
self.r_disp_readtime = time.time()
if written != len(kiss_command):
raise IOError("An IO error occurred while sending display read command")
def _read_display_job(self):
while self.should_read_display:
self.read_display()
time.sleep(self.read_display_interval)
def start_display_updates(self):
if not self.should_read_display:
self.should_read_display = True
threading.Thread(target=self._read_display_job, daemon=True).start()
def stop_display_updates(self):
self.should_read_display = False
def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.write_mux(kiss_command)
@@ -1199,16 +1249,31 @@ class RNodeInterface(Interface):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 8):
if (len(command_buffer) == 11):
ats = command_buffer[0] << 8 | command_buffer[1]
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
crs = command_buffer[8]
nfl = command_buffer[9]
ntf = command_buffer[10]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
self.r_channel_load_long = cul/100.0
self.r_current_rssi = crs-RNodeInterface.RSSI_OFFSET
self.r_noise_floor = nfl-RNodeInterface.RSSI_OFFSET
if ntf == 0xFF:
self.r_interference = None
else:
self.r_interference = ntf-RNodeInterface.RSSI_OFFSET
if self.r_interference != None:
RNS.log(f"{self} Radio detected interference at {self.r_interference} dBm", RNS.LOG_DEBUG)
# TODO: Remove debug
# RNS.log(f"RSSI: {self.r_current_rssi}, Noise floor: {self.r_noise_floor}, Interference: {self.r_interference}", RNS.LOG_EXTREME)
elif (command == KISS.CMD_STAT_PHYPRM):
if (byte == KISS.FESC):
escape = True
@@ -1220,22 +1285,49 @@ class RNodeInterface(Interface):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 10):
if (len(command_buffer) == 12):
lst = (command_buffer[0] << 8 | command_buffer[1])/1000.0
lsr = command_buffer[2] << 8 | command_buffer[3]
prs = command_buffer[4] << 8 | command_buffer[5]
prt = command_buffer[6] << 8 | command_buffer[7]
cst = command_buffer[8] << 8 | command_buffer[9]
dft = command_buffer[10] << 8 | command_buffer[11]
if lst != self.r_symbol_time_ms or lsr != self.r_symbol_rate or prs != self.r_preamble_symbols or prt != self.r_premable_time_ms or cst != self.r_csma_slot_time_ms:
if lst != self.r_symbol_time_ms or lsr != self.r_symbol_rate or prs != self.r_preamble_symbols or prt != self.r_premable_time_ms or cst != self.r_csma_slot_time_ms or dft != self.r_csma_difs_ms:
self.r_symbol_time_ms = lst
self.r_symbol_rate = lsr
self.r_preamble_symbols = prs
self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst
RNS.log(str(self)+" Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms (at "+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
self.r_csma_difs_ms = dft
RNS.log(f"{self} Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms ("+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting DIFS time is "+str(self.r_csma_difs_ms)+"ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CSMA):
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) == 3):
cbw = command_buffer[0]
cbl = command_buffer[1]
cbh = command_buffer[2]
if cbw != self.r_csma_cw_band or cbl != self.r_csma_cw_min or cbh != self.r_csma_cw_max:
self.r_csma_cw_band = cbw
self.r_csma_cw_min = cbl
self.r_csma_cw_max = cbh
# TODO: Remove debug
# RNS.log(f"{self} Radio reporting contention window band is {self.r_csma_cw_band}", RNS.LOG_EXTREME)
# RNS.log(f"{self} Radio reporting minimum contention window is {self.r_csma_cw_min}", RNS.LOG_EXTREME)
# RNS.log(f"{self} Radio reporting maximum contention window is {self.r_csma_cw_max}", RNS.LOG_EXTREME)
elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC):
escape = True
@@ -1285,6 +1377,37 @@ class RNodeInterface(Interface):
raise IOError("ESP32 reset")
elif (command == KISS.CMD_READY):
self.process_queue()
elif (command == KISS.CMD_FB_READ):
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) == 512):
self.r_framebuffer_latency = time.time() - self.r_framebuffer_readtime
self.r_framebuffer = command_buffer
elif (command == KISS.CMD_DISP_READ):
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) == 1024):
self.r_disp_latency = time.time() - self.r_disp_readtime
self.r_disp = command_buffer
elif (command == KISS.CMD_DETECT):
if byte == KISS.DETECT_RESP:
self.detected = True
+4 -2
View File
@@ -23,5 +23,7 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
py_modules = glob.glob(os.path.dirname(__file__)+"/*.py")
pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
modules = py_modules+pyc_modules
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
+19 -14
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2024 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
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from collections import deque
import socketserver
import threading
@@ -456,21 +456,23 @@ class AutoInterface(Interface):
self.peers[addr][1] = time.time()
def process_incoming(self, data):
data_hash = RNS.Identity.full_hash(data)
deque_hit = False
if data_hash in self.mif_deque:
for te in self.mif_deque_times:
if te[0] == data_hash and time.time() < te[1]+AutoInterface.MULTI_IF_DEQUE_TTL:
deque_hit = True
break
if self.online:
data_hash = RNS.Identity.full_hash(data)
deque_hit = False
if data_hash in self.mif_deque:
for te in self.mif_deque_times:
if te[0] == data_hash and time.time() < te[1]+AutoInterface.MULTI_IF_DEQUE_TTL:
deque_hit = True
break
if not deque_hit:
self.mif_deque.append(data_hash)
self.mif_deque_times.append([data_hash, time.time()])
self.rxb += len(data)
self.owner.inbound(data, self)
if not deque_hit:
self.mif_deque.append(data_hash)
self.mif_deque_times.append([data_hash, time.time()])
self.rxb += len(data)
self.owner.inbound(data, self)
def process_outgoing(self,data):
if self.online:
for peer in self.peers:
try:
if self.outbound_udp_socket == None:
@@ -492,6 +494,9 @@ class AutoInterface(Interface):
def should_ingress_limit(self):
return False
def detach(self):
self.online = False
def __str__(self):
return "AutoInterface["+self.name+"]"
+1 -1
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
import socketserver
import threading
import platform
+34 -5
View File
@@ -64,12 +64,16 @@ class Interface:
IC_BURST_PENALTY = 5*60
IC_HELD_RELEASE_INTERVAL = 30
AUTOCONFIGURE_MTU = False
def __init__(self):
self.rxb = 0
self.txb = 0
self.created = time.time()
self.online = False
self.bitrate = 1e6
self.rxb = 0
self.txb = 0
self.created = time.time()
self.detached = False
self.online = False
self.bitrate = 62500
self.HW_MTU = None
self.ingress_control = True
self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES
@@ -117,6 +121,31 @@ class Interface:
else:
return False
def optimise_mtu(self):
if self.AUTOCONFIGURE_MTU:
if self.bitrate > 16_000_000:
self.HW_MTU = 262144
elif self.bitrate > 8_000_000:
self.HW_MTU = 131072
elif self.bitrate > 4_000_000:
self.HW_MTU = 65536
elif self.bitrate > 2_000_000:
self.HW_MTU = 32768
elif self.bitrate > 1_000_000:
self.HW_MTU = 16384
elif self.bitrate > 500_000:
self.HW_MTU = 8192
elif self.bitrate > 250_000:
self.HW_MTU = 4096
elif self.bitrate > 125_000:
self.HW_MTU = 2048
elif self.bitrate > 62_500:
self.HW_MTU = 1024
else:
self.HW_MTU = None
RNS.log(f"{self} hardware MTU set to {self.HW_MTU}", RNS.LOG_DEBUG) # TODO: Remove debug
def age(self):
return time.time()-self.created
+1 -1
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from time import sleep
import sys
import threading
+29 -28
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
import socketserver
import threading
import socket
@@ -52,14 +52,12 @@ class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
class LocalClientInterface(Interface):
RECONNECT_WAIT = 8
AUTOCONFIGURE_MTU = True
def __init__(self, owner, name, target_port = None, connected_socket=None):
super().__init__()
# TODO: Remove at some point
# self.rxptime = 0
self.HW_MTU = 1064
self.HW_MTU = 262144
self.online = False
@@ -89,7 +87,7 @@ class LocalClientInterface(Interface):
self.connect()
self.owner = owner
self.bitrate = 1000*1000*1000
self.bitrate = 1_000_000_000
self.online = True
self.writing = False
@@ -197,32 +195,31 @@ class LocalClientInterface(Interface):
try:
in_frame = False
escape = False
frame_buffer = b""
data_in = b""
data_buffer = b""
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 (in_frame and byte == HDLC.FLAG):
in_frame = False
self.process_incoming(data_buffer)
elif (byte == HDLC.FLAG):
in_frame = True
data_buffer = b""
elif (in_frame and len(data_buffer) < self.HW_MTU):
if (byte == HDLC.ESC):
escape = True
frame_buffer += data_in
flags_remaining = True
while flags_remaining:
frame_start = frame_buffer.find(HDLC.FLAG)
if frame_start != -1:
frame_end = frame_buffer.find(HDLC.FLAG, frame_start+1)
if frame_end != -1:
frame = frame_buffer[frame_start+1:frame_end]
frame = frame.replace(bytes([HDLC.ESC, HDLC.FLAG ^ HDLC.ESC_MASK]), bytes([HDLC.FLAG]))
frame = frame.replace(bytes([HDLC.ESC, HDLC.ESC ^ HDLC.ESC_MASK]), bytes([HDLC.ESC]))
if len(frame) > RNS.Reticulum.HEADER_MINSIZE:
self.process_incoming(frame)
frame_buffer = frame_buffer[frame_end:]
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])
flags_remaining = False
else:
flags_remaining = False
else:
self.online = False
if self.is_connected_to_shared_instance and not self.detached:
@@ -249,12 +246,14 @@ class LocalClientInterface(Interface):
self.detached = True
try:
self.socket.shutdown(socket.SHUT_RDWR)
if self.socket != None:
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()
if self.socket != None:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
@@ -292,6 +291,7 @@ class LocalClientInterface(Interface):
class LocalServerInterface(Interface):
AUTOCONFIGURE_MTU = True
def __init__(self, owner, bindport=None):
super().__init__()
@@ -319,6 +319,7 @@ class LocalServerInterface(Interface):
address = (self.bind_ip, self.bind_port)
self.server = ThreadingTCPServer(address, handlerFactory(self.incoming_connection))
self.server.daemon_threads = True
thread = threading.Thread(target=self.server.serve_forever)
thread.daemon = True
+1 -1
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from time import sleep
import sys
import threading
+133 -12
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from time import sleep
import sys
import threading
@@ -55,10 +55,12 @@ class KISS():
CMD_STAT_CHTM = 0x25
CMD_STAT_PHYPRM = 0x26
CMD_STAT_BAT = 0x27
CMD_STAT_CSMA = 0x28
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_FB_EXT = 0x41
CMD_FB_READ = 0x42
CMD_DISP_READ = 0x66
CMD_FB_WRITE = 0x43
CMD_BT_CTRL = 0x46
CMD_PLATFORM = 0x48
@@ -118,6 +120,8 @@ class RNodeInterface(Interface):
BATTERY_STATE_CHARGING = 0x02
BATTERY_STATE_CHARGED = 0x03
DISPLAY_READ_INTERVAL = 1.0
def __init__(self, owner, configuration):
if RNS.vendor.platformutils.is_android():
raise SystemError("Invalid interface type. The Android-specific RNode interface must be used on Android")
@@ -181,6 +185,7 @@ class RNodeInterface(Interface):
self.online = False
self.detached = False
self.reconnecting= False
self.hw_errors = []
self.use_ble = False
self.ble_name = ble_name
@@ -230,12 +235,29 @@ class RNodeInterface(Interface):
self.r_airtime_long = 0.0
self.r_channel_load_short = 0.0
self.r_channel_load_long = 0.0
self.r_symbol_time_ms = None
self.r_symbol_rate = None
self.r_preamble_symbols = None
self.r_premable_time_ms = None
self.r_symbol_time_ms = None
self.r_symbol_rate = None
self.r_preamble_symbols = None
self.r_premable_time_ms = None
self.r_csma_slot_time_ms = None
self.r_csma_difs_ms = None
self.r_csma_cw_band = None
self.r_csma_cw_min = None
self.r_csma_cw_max = None
self.r_current_rssi = None
self.r_noise_floor = None
self.r_battery_state = RNodeInterface.BATTERY_STATE_UNKNOWN
self.r_battery_percent = 0
self.r_framebuffer = b""
self.r_framebuffer_readtime = 0
self.r_framebuffer_latency = 0
self.r_disp = b""
self.r_disp_readtime = 0
self.r_disp_latency = 0
self.should_read_display = False
self.read_display_interval = RNodeInterface.DISPLAY_READ_INTERVAL
self.packet_queue = []
self.flow_control = flow_control
@@ -453,7 +475,34 @@ class RNodeInterface(Interface):
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device")
raise IOError("An IO error occurred while writing framebuffer data to device")
def read_framebuffer(self):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_READ])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
self.r_framebuffer_readtime = time.time()
if written != len(kiss_command):
raise IOError("An IO error occurred while sending framebuffer read command")
def read_display(self):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_READ])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
self.r_disp_readtime = time.time()
if written != len(kiss_command):
raise IOError("An IO error occurred while sending display read command")
def _read_display_job(self):
while self.should_read_display:
self.read_display()
time.sleep(self.read_display_interval)
def start_display_updates(self):
if not self.should_read_display:
self.should_read_display = True
threading.Thread(target=self._read_display_job, daemon=True).start()
def stop_display_updates(self):
self.should_read_display = False
def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
@@ -830,16 +879,31 @@ class RNodeInterface(Interface):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 8):
if (len(command_buffer) == 11):
ats = command_buffer[0] << 8 | command_buffer[1]
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
crs = command_buffer[8]
nfl = command_buffer[9]
ntf = command_buffer[10]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
self.r_channel_load_long = cul/100.0
self.r_current_rssi = crs-RNodeInterface.RSSI_OFFSET
self.r_noise_floor = nfl-RNodeInterface.RSSI_OFFSET
if ntf == 0xFF:
self.r_interference = None
else:
self.r_interference = ntf-RNodeInterface.RSSI_OFFSET
if self.r_interference != None:
RNS.log(f"{self} Radio detected interference at {self.r_interference} dBm", RNS.LOG_DEBUG)
# TODO: Remove debug
# RNS.log(f"RSSI: {self.r_current_rssi}, Noise floor: {self.r_noise_floor}, Interference: {self.r_interference}", RNS.LOG_EXTREME)
elif (command == KISS.CMD_STAT_PHYPRM):
if (byte == KISS.FESC):
escape = True
@@ -851,22 +915,49 @@ class RNodeInterface(Interface):
byte = KISS.FESC
escape = False
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 10):
if (len(command_buffer) == 12):
lst = (command_buffer[0] << 8 | command_buffer[1])/1000.0
lsr = command_buffer[2] << 8 | command_buffer[3]
prs = command_buffer[4] << 8 | command_buffer[5]
prt = command_buffer[6] << 8 | command_buffer[7]
cst = command_buffer[8] << 8 | command_buffer[9]
dft = command_buffer[10] << 8 | command_buffer[11]
if lst != self.r_symbol_time_ms or lsr != self.r_symbol_rate or prs != self.r_preamble_symbols or prt != self.r_premable_time_ms or cst != self.r_csma_slot_time_ms:
if lst != self.r_symbol_time_ms or lsr != self.r_symbol_rate or prs != self.r_preamble_symbols or prt != self.r_premable_time_ms or cst != self.r_csma_slot_time_ms or dft != self.r_csma_difs_ms:
self.r_symbol_time_ms = lst
self.r_symbol_rate = lsr
self.r_preamble_symbols = prs
self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst
RNS.log(str(self)+" Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms (at "+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
self.r_csma_difs_ms = dft
RNS.log(f"{self} Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms ("+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting DIFS time is "+str(self.r_csma_difs_ms)+"ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CSMA):
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) == 3):
cbw = command_buffer[0]
cbl = command_buffer[1]
cbh = command_buffer[2]
if cbw != self.r_csma_cw_band or cbl != self.r_csma_cw_min or cbh != self.r_csma_cw_max:
self.r_csma_cw_band = cbw
self.r_csma_cw_min = cbl
self.r_csma_cw_max = cbh
# TODO: Remove debug
# RNS.log(f"{self} Radio reporting contention window band is {self.r_csma_cw_band}", RNS.LOG_EXTREME)
# RNS.log(f"{self} Radio reporting minimum contention window is {self.r_csma_cw_min}", RNS.LOG_EXTREME)
# RNS.log(f"{self} Radio reporting maximum contention window is {self.r_csma_cw_max}", RNS.LOG_EXTREME)
elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC):
escape = True
@@ -916,6 +1007,36 @@ class RNodeInterface(Interface):
raise IOError("ESP32 reset")
elif (command == KISS.CMD_READY):
self.process_queue()
elif (command == KISS.CMD_FB_READ):
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) == 512):
self.r_framebuffer_latency = time.time() - self.r_framebuffer_readtime
self.r_framebuffer = command_buffer
elif (command == KISS.CMD_DISP_READ):
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) == 1024):
self.r_disp_latency = time.time() - self.r_disp_readtime
self.r_disp = command_buffer
elif (command == KISS.CMD_DETECT):
if byte == KISS.DETECT_RESP:
self.detected = True
+6 -8
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from time import sleep
import sys
import threading
@@ -197,14 +197,13 @@ class RNodeMultiInterface(Interface):
# Count how many interfaces are in the file
for subinterface in c:
# if the retrieved entry is not a string, it must be a dictionary, which is what we want
if not isinstance(c[subinterface], str):
if isinstance(c[subinterface], dict):
count += 1
# Count how many interfaces are enabled to allow for appropriate matrix sizing
for subinterface in c:
# if the retrieved entry is not a string, it must be a dictionary, which is what we want
if not isinstance(c[subinterface], str):
subinterface_config = self.config["interfaces"][name][subinterface]
if isinstance(c[subinterface], dict):
subinterface_config = c[subinterface]
if (("interface_enabled" in subinterface_config) and subinterface_config.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True):
enabled_count += 1
@@ -213,9 +212,8 @@ class RNodeMultiInterface(Interface):
subint_index = 0
for subinterface in c:
# If the retrieved entry is not a string, it must be a dictionary, which is what we want
if not isinstance(c[subinterface], str):
subinterface_config = self.config["interfaces"][name][subinterface]
if isinstance(c[subinterface], dict):
subinterface_config = c[subinterface]
if (("interface_enabled" in subinterface_config) and subinterface_config.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True):
subint_config[subint_index][0] = subinterface
+1 -1
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
from time import sleep
import sys
import threading
+53 -42
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
import socketserver
import threading
import platform
@@ -30,6 +30,9 @@ import sys
import os
import RNS
class TCPInterface():
HW_MTU = 262144
class HDLC():
FLAG = 0x7E
ESC = 0x7D
@@ -64,6 +67,7 @@ class ThreadingTCP6Server(socketserver.ThreadingMixIn, socketserver.TCPServer):
class TCPClientInterface(Interface):
BITRATE_GUESS = 10*1000*1000
DEFAULT_IFAC_SIZE = 16
AUTOCONFIGURE_MTU = True
RECONNECT_WAIT = 5
RECONNECT_MAX_TRIES = None
@@ -96,8 +100,7 @@ class TCPClientInterface(Interface):
connect_timeout = c.as_int("connect_timeout") if "connect_timeout" in c else None
max_reconnect_tries = c.as_int("max_reconnect_tries") if "max_reconnect_tries" in c else None
self.HW_MTU = 1064
self.HW_MTU = TCPInterface.HW_MTU
self.IN = True
self.OUT = False
self.socket = None
@@ -192,19 +195,21 @@ class TCPClientInterface(Interface):
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
def detach(self):
self.online = False
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)
if self.socket != None:
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()
if self.socket != None:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
@@ -285,14 +290,15 @@ class TCPClientInterface(Interface):
raise IOError("Attempt to reconnect on a non-initiator TCP interface")
def process_incoming(self, data):
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data)
self.owner.inbound(data, self)
if self.online and not self.detached:
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data)
self.owner.inbound(data, self)
def process_outgoing(self, data):
if self.online:
if self.online and not self.detached:
# while self.writing:
# time.sleep(0.01)
@@ -320,19 +326,19 @@ class TCPClientInterface(Interface):
try:
in_frame = False
escape = False
frame_buffer = b""
data_in = b""
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 self.kiss_framing:
# Read loop for KISS framing
pointer = 0
while pointer < len(data_in):
byte = data_in[pointer]
pointer += 1
if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA):
in_frame = False
self.process_incoming(data_buffer)
@@ -358,25 +364,26 @@ class TCPClientInterface(Interface):
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.process_incoming(data_buffer)
elif (byte == HDLC.FLAG):
in_frame = True
data_buffer = b""
elif (in_frame and len(data_buffer) < self.HW_MTU):
if (byte == HDLC.ESC):
escape = True
else:
# Read loop for standard HDLC framing
frame_buffer += data_in
flags_remaining = True
while flags_remaining:
frame_start = frame_buffer.find(HDLC.FLAG)
if frame_start != -1:
frame_end = frame_buffer.find(HDLC.FLAG, frame_start+1)
if frame_end != -1:
frame = frame_buffer[frame_start+1:frame_end]
frame = frame.replace(bytes([HDLC.ESC, HDLC.FLAG ^ HDLC.ESC_MASK]), bytes([HDLC.FLAG]))
frame = frame.replace(bytes([HDLC.ESC, HDLC.ESC ^ HDLC.ESC_MASK]), bytes([HDLC.ESC]))
if len(frame) > RNS.Reticulum.HEADER_MINSIZE:
self.process_incoming(frame)
frame_buffer = frame_buffer[frame_end:]
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])
flags_remaining = False
else:
flags_remaining = False
else:
self.online = False
if self.initiator and not self.detached:
@@ -431,8 +438,9 @@ class TCPClientInterface(Interface):
class TCPServerInterface(Interface):
BITRATE_GUESS = 10*1000*1000
BITRATE_GUESS = 10_000_000
DEFAULT_IFAC_SIZE = 16
AUTOCONFIGURE_MTU = True
@staticmethod
def get_address_for_if(name, bind_port, prefer_ipv6=False):
@@ -484,7 +492,7 @@ class TCPServerInterface(Interface):
if port != None:
bindport = port
self.HW_MTU = 1064
self.HW_MTU = TCPInterface.HW_MTU
self.online = False
self.spawned_interfaces = []
@@ -531,6 +539,7 @@ class TCPServerInterface(Interface):
else:
ThreadingTCPServer.allow_reuse_address = True
self.server = ThreadingTCPServer(bind_address, handlerFactory(self.incoming_connection))
self.server.daemon_threads = True
self.bitrate = TCPServerInterface.BITRATE_GUESS
@@ -553,6 +562,7 @@ class TCPServerInterface(Interface):
spawned_interface.target_port = str(handler.client_address[1])
spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate
spawned_interface.optimise_mtu()
spawned_interface.ifac_size = self.ifac_size
spawned_interface.ifac_netname = self.ifac_netname
@@ -596,15 +606,16 @@ class TCPServerInterface(Interface):
def process_outgoing(self, data):
pass
def detach(self):
self.detached = True
self.online = False
if self.server != None:
if hasattr(self.server, "shutdown"):
if callable(self.server.shutdown):
try:
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
self.server.shutdown()
self.detached = True
self.server.server_close()
self.server = None
except Exception as e:
+1 -1
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .Interface import Interface
from RNS.Interfaces.Interface import Interface
import socketserver
import threading
import socket
+4 -2
View File
@@ -24,5 +24,7 @@ import os
import glob
import RNS.Interfaces.Android
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
py_modules = glob.glob(os.path.dirname(__file__)+"/*.py")
pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
modules = py_modules+pyc_modules
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
+99 -13
View File
@@ -28,6 +28,7 @@ from time import sleep
from .vendor import umsgpack as umsgpack
import threading
import inspect
import struct
import math
import time
import RNS
@@ -68,8 +69,9 @@ class Link:
Timeout for link establishment in seconds per hop to destination.
"""
TRAFFIC_TIMEOUT_MIN_MS = 5
TRAFFIC_TIMEOUT_FACTOR = 6
LINK_MTU_SIZE = 3
TRAFFIC_TIMEOUT_MIN_MS = 5
TRAFFIC_TIMEOUT_FACTOR = 6
KEEPALIVE_TIMEOUT_FACTOR = 4
"""
RTT timeout factor used in link timeout calculation.
@@ -106,16 +108,46 @@ class Link:
ACCEPT_ALL = 0x02
resource_strategies = [ACCEPT_NONE, ACCEPT_APP, ACCEPT_ALL]
@staticmethod
def mtu_bytes(mtu):
return struct.pack(">I", mtu & 0xFFFFFF)[1:]
@staticmethod
def mtu_from_lr_packet(packet):
if len(packet.data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE:
return (packet.data[Link.ECPUBSIZE] << 16) + (packet.data[Link.ECPUBSIZE+1] << 8) + (packet.data[Link.ECPUBSIZE+2])
else:
return None
@staticmethod
def mtu_from_lp_packet(packet):
if len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2+Link.LINK_MTU_SIZE:
mtu_bytes = packet.data[RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2+Link.LINK_MTU_SIZE]
return (mtu_bytes[0] << 16) + (mtu_bytes[1] << 8) + (mtu_bytes[2])
else:
return None
@staticmethod
def validate_request(owner, data, packet):
if len(data) == (Link.ECPUBSIZE):
if len(data) == Link.ECPUBSIZE or len(data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE:
try:
link = Link(owner = owner, peer_pub_bytes=data[:Link.ECPUBSIZE//2], peer_sig_pub_bytes=data[Link.ECPUBSIZE//2:Link.ECPUBSIZE])
link.set_link_id(packet)
if len(data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE:
RNS.log("Link request includes MTU signalling", RNS.LOG_DEBUG) # TODO: Remove debug
try:
link.mtu = Link.mtu_from_lr_packet(packet) or Reticulum.MTU
except Exception as e:
RNS.trace_exception(e)
link.mtu = RNS.Reticulum.MTU
link.update_mdu()
link.destination = packet.destination
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE
link.establishment_cost += len(packet.raw)
RNS.log("Validating link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE)
RNS.log(f"Validating link request {RNS.prettyhexrep(link.link_id)}", RNS.LOG_VERBOSE)
RNS.log(f"Link MTU configured to {RNS.prettysize(link.mtu)}", RNS.LOG_EXTREME)
RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_EXTREME)
link.handshake()
link.attached_interface = packet.receiving_interface
@@ -134,7 +166,7 @@ class Link:
return None
else:
RNS.log("Invalid link request payload size, dropping request", RNS.LOG_DEBUG)
RNS.log(f"Invalid link request payload size of {len(data)} bytes, dropping request", RNS.LOG_DEBUG)
return None
@@ -142,10 +174,13 @@ class Link:
if destination != None and destination.type != RNS.Destination.SINGLE:
raise TypeError("Links can only be established to the \"single\" destination type")
self.rtt = None
self.mtu = RNS.Reticulum.MTU
self.establishment_cost = 0
self.establishment_rate = None
self.callbacks = LinkCallbacks()
self.resource_strategy = Link.ACCEPT_NONE
self.last_resource_window = None
self.last_resource_eifr = None
self.outgoing_resources = []
self.incoming_resources = []
self.pending_requests = []
@@ -208,8 +243,13 @@ class Link:
if closed_callback != None:
self.set_link_closed_callback(closed_callback)
if (self.initiator):
self.request_data = self.pub_bytes+self.sig_pub_bytes
if self.initiator:
link_mtu = b""
nh_hw_mtu = RNS.Transport.next_hop_interface_hw_mtu(destination.hash)
if RNS.Reticulum.link_mtu_discovery() and nh_hw_mtu:
link_mtu = Link.mtu_bytes(nh_hw_mtu)
RNS.log(f"Signalling link MTU of {RNS.prettysize(nh_hw_mtu)} for link", RNS.LOG_DEBUG) # TODO: Remove debug
self.request_data = self.pub_bytes+self.sig_pub_bytes+link_mtu
self.packet = RNS.Packet(destination, self.request_data, packet_type=RNS.Packet.LINKREQUEST)
self.packet.pack()
self.establishment_cost += len(self.packet.raw)
@@ -233,8 +273,17 @@ class Link:
if not hasattr(self.peer_pub, "curve"):
self.peer_pub.curve = Link.CURVE
@staticmethod
def link_id_from_lr_packet(packet):
hashable_part = packet.get_hashable_part()
if len(packet.data) > Link.ECPUBSIZE:
diff = len(packet.data) - Link.ECPUBSIZE
hashable_part = hashable_part[:-diff]
return RNS.Identity.truncated_hash(hashable_part)
def set_link_id(self, packet):
self.link_id = packet.getTruncatedHash()
self.link_id = Link.link_id_from_lr_packet(packet)
self.hash = self.link_id
def handshake(self):
@@ -253,10 +302,14 @@ class Link:
def prove(self):
signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes
mtu_bytes = b""
if self.mtu != RNS.Reticulum.MTU:
mtu_bytes = Link.mtu_bytes(self.mtu)
signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes+mtu_bytes
signature = self.owner.identity.sign(signed_data)
proof_data = signature+self.pub_bytes
proof_data = signature+self.pub_bytes+mtu_bytes
proof = RNS.Packet(self, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.LRPROOF)
proof.send()
self.establishment_cost += len(proof.raw)
@@ -279,6 +332,14 @@ class Link:
def validate_proof(self, packet):
try:
if self.status == Link.PENDING:
mtu_bytes = b""
confirmed_mtu = None
if len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2+Link.LINK_MTU_SIZE:
confirmed_mtu = Link.mtu_from_lp_packet(packet)
mtu_bytes = Link.mtu_bytes(confirmed_mtu)
packet.data = packet.data[:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2]
RNS.log(f"Destination confirmed link MTU of {RNS.prettysize(confirmed_mtu)}", RNS.LOG_DEBUG) # TODO: Remove debug
if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2:
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2]
peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE]
@@ -286,7 +347,7 @@ class Link:
self.handshake()
self.establishment_cost += len(packet.raw)
signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes
signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes+mtu_bytes
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if self.destination.identity.validate(signature, signed_data):
@@ -296,6 +357,8 @@ class Link:
self.rtt = time.time() - self.request_time
self.attached_interface = packet.receiving_interface
self.__remote_identity = self.destination.identity
self.mtu = confirmed_mtu or RNS.Reticulum.MTU
self.update_mdu()
self.status = Link.ACTIVE
self.activated_at = time.time()
self.last_proof = self.activated_at
@@ -361,7 +424,7 @@ class Link:
if timeout == None:
timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME*1.125
if len(packed_request) <= Link.MDU:
if len(packed_request) <= self.mdu:
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
packet_receipt = request_packet.send()
@@ -395,6 +458,10 @@ class Link:
)
def update_mdu(self):
self.mdu = self.mtu - RNS.Reticulum.HEADER_MAXSIZE - RNS.Reticulum.IFAC_MIN_SIZE
self.mdu = math.floor((self.mtu-RNS.Reticulum.IFAC_MIN_SIZE-RNS.Reticulum.HEADER_MINSIZE-RNS.Identity.TOKEN_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
def rtt_packet(self, packet):
try:
measured_rtt = time.time() - self.request_time
@@ -709,7 +776,7 @@ class Link:
if response != None:
packed_response = umsgpack.packb([request_id, response])
if len(packed_response) <= Link.MDU:
if len(packed_response) <= self.mdu:
RNS.Packet(self, packed_response, RNS.Packet.DATA, context = RNS.Packet.RESPONSE).send()
else:
response_resource = RNS.Resource(packed_response, self, request_id = request_id, is_response = True)
@@ -895,6 +962,8 @@ class Link:
resource_advertisement.link = self
if self.callbacks.resource(resource_advertisement):
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
else:
RNS.Resource.reject(packet)
except Exception as e:
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
elif self.resource_strategy == Link.ACCEPT_ALL:
@@ -940,6 +1009,15 @@ class Link:
if resource_hash == resource.hash:
resource.cancel()
elif packet.context == RNS.Packet.RESOURCE_RCL:
plaintext = self.decrypt(packet.data)
if plaintext != None:
self.__update_phy_stats(packet)
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
for resource in self.outgoing_resources:
if resource_hash == resource.hash:
resource._rejected()
elif packet.context == RNS.Packet.KEEPALIVE:
if not self.initiator and packet.data == bytes([0xFF]):
keepalive_packet = RNS.Packet(self, bytes([0xFE]), context=RNS.Packet.KEEPALIVE)
@@ -1076,6 +1154,8 @@ class Link:
def resource_concluded(self, resource):
if resource in self.incoming_resources:
self.last_resource_window = resource.window
self.last_resource_eifr = resource.eifr
self.incoming_resources.remove(resource)
if resource in self.outgoing_resources:
self.outgoing_resources.remove(resource)
@@ -1105,6 +1185,12 @@ class Link:
return False
def get_last_resource_window(self):
return self.last_resource_window
def get_last_resource_eifr(self):
return self.last_resource_eifr
def cancel_outgoing_resource(self, resource):
if resource in self.outgoing_resources:
self.outgoing_resources.remove(resource)
+9 -1
View File
@@ -137,7 +137,11 @@ class Packet:
self.fromPacked = True
self.create_receipt = False
self.MTU = RNS.Reticulum.MTU
if destination and destination.type == RNS.Destination.LINK:
self.MTU = destination.mtu
else:
self.MTU = RNS.Reticulum.MTU
self.sent_at = None
self.packet_hash = None
self.ratchet_id = None
@@ -501,6 +505,10 @@ class PacketReceipt:
return False
elif len(proof) == PacketReceipt.IMPL_LENGTH:
# This is an implicit proof
if not hasattr(self.destination, "identity"):
return False
if self.destination.identity == None:
return False
+78 -16
View File
@@ -141,6 +141,19 @@ class Resource:
COMPLETE = 0x06
FAILED = 0x07
CORRUPT = 0x08
REJECTED = 0x00
@staticmethod
def reject(advertisement_packet):
try:
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
resource_hash = adv.h
reject_packet = RNS.Packet(advertisement_packet.link, resource_hash, context=RNS.Packet.RESOURCE_RCL)
reject_packet.send()
except Exception as e:
RNS.log(f"An error ocurred while rejecting advertised resource: {e}", RNS.LOG_ERROR)
RNS.trace_exception(e)
@staticmethod
def accept(advertisement_packet, callback=None, progress_callback = None, request_id = None):
@@ -163,7 +176,7 @@ class Resource:
resource.initiator = False
resource.callback = callback
resource.__progress_callback = progress_callback
resource.total_parts = int(math.ceil(resource.size/float(Resource.SDU)))
resource.total_parts = int(math.ceil(resource.size/float(resource.sdu)))
resource.received_count = 0
resource.outstanding_parts = 0
resource.parts = [None] * resource.total_parts
@@ -186,6 +199,13 @@ class Resource:
resource.waiting_for_hmu = False
resource.receiving_part = False
resource.consecutive_completed_height = -1
previous_window = resource.link.get_last_resource_window()
previous_eifr = resource.link.get_last_resource_eifr()
if previous_window:
resource.window = previous_window
if previous_eifr:
resource.previous_eifr = previous_eifr
if not resource.link.has_incoming_resource(resource):
resource.link.register_incoming_resource(resource)
@@ -271,6 +291,10 @@ class Resource:
self.status = Resource.NONE
self.link = link
if self.link.mtu:
self.sdu = self.link.mtu - RNS.Reticulum.HEADER_MAXSIZE - RNS.Reticulum.IFAC_MIN_SIZE
else:
self.sdu = link.mdu or Resource.SDU
self.max_retries = Resource.MAX_RETRIES
self.max_adv_retries = Resource.MAX_ADV_RETRIES
self.retries_left = self.max_retries
@@ -286,10 +310,14 @@ class Resource:
self.req_sent = 0
self.req_resp_rtt_rate = 0
self.rtt_rxd_bytes_at_part_req = 0
self.req_data_rtt_rate = 0
self.eifr = None
self.previous_eifr = None
self.fast_rate_rounds = 0
self.very_slow_rate_rounds = 0
self.request_id = request_id
self.is_response = is_response
self.auto_compress = auto_compress
self.req_hashlist = []
self.receiver_min_consecutive_height = 0
@@ -346,7 +374,7 @@ class Resource:
self.size = len(self.data)
self.sent_parts = 0
hashmap_entries = int(math.ceil(self.size/float(Resource.SDU)))
hashmap_entries = int(math.ceil(self.size/float(self.sdu)))
self.total_parts = hashmap_entries
hashmap_ok = False
@@ -368,7 +396,7 @@ class Resource:
self.hashmap = b""
collision_guard_list = []
for i in range(0,hashmap_entries):
data = self.data[i*Resource.SDU:(i+1)*Resource.SDU]
data = self.data[i*self.sdu:(i+1)*self.sdu]
map_hash = self.get_map_hash(data)
if map_hash in collision_guard_list:
@@ -455,6 +483,22 @@ class Resource:
self.watchdog_job()
def update_eifr(self):
if self.rtt == None:
rtt = self.link.rtt
else:
rtt = self.rtt
if self.req_data_rtt_rate != 0:
expected_inflight_rate = self.req_data_rtt_rate*8
else:
if self.previous_eifr != None:
expected_inflight_rate = self.previous_eifr
else:
expected_inflight_rate = self.link.establishment_cost*8 / rtt
self.eifr = expected_inflight_rate
def watchdog_job(self):
thread = threading.Thread(target=self.__watchdog_job)
thread.daemon = True
@@ -469,7 +513,6 @@ class Resource:
sleep(0.025)
sleep_time = None
if self.status == Resource.ADVERTISED:
sleep_time = (self.adv_sent+self.timeout+Resource.PROCESSING_GRACE)-time.time()
if sleep_time < 0:
@@ -493,17 +536,19 @@ class Resource:
elif self.status == Resource.TRANSFERRING:
if not self.initiator:
if self.rtt == None:
rtt = self.link.rtt
else:
rtt = self.rtt
window_remaining = self.outstanding_parts
retries_used = self.max_retries - self.retries_left
extra_wait = retries_used * Resource.PER_RETRY_DELAY
sleep_time = self.last_activity + (rtt*(self.part_timeout_factor+window_remaining)) + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
self.update_eifr()
expected_tof_remaining = (self.outstanding_parts*self.sdu*8)/self.eifr
if self.req_resp_rtt_rate != 0:
sleep_time = self.last_activity + self.part_timeout_factor*expected_tof_remaining + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
else:
sleep_time = self.last_activity + self.part_timeout_factor*((3*self.sdu)/self.eifr) + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
# RNS.log(f"EIFR {RNS.prettyspeed(self.eifr)}, ETOF {RNS.prettyshorttime(expected_tof_remaining)} ", RNS.LOG_DEBUG, pt=True)
# RNS.log(f"Resource ST {RNS.prettyshorttime(sleep_time)}, RTT {RNS.prettyshorttime(self.rtt or self.link.rtt)}, {self.outstanding_parts} left", RNS.LOG_DEBUG, pt=True)
if sleep_time < 0:
if self.retries_left > 0:
@@ -553,8 +598,11 @@ class Resource:
self.last_part_sent = time.time()
sleep_time = 0.001
elif self.status == Resource.REJECTED:
sleep_time = 0.001
if sleep_time == 0:
RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_WARNING)
RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_DEBUG)
if sleep_time == None or sleep_time < 0:
RNS.log("Timing error, cancelling resource transfer.", RNS.LOG_ERROR)
self.cancel()
@@ -647,6 +695,7 @@ class Resource:
request_id = self.request_id,
is_response = self.is_response,
advertise = False,
auto_compress = self.auto_compress,
)
def validate_proof(self, proof_data):
@@ -770,6 +819,7 @@ class Resource:
if rtt != 0:
self.req_data_rtt_rate = req_transferred/rtt
self.update_eifr()
self.rtt_rxd_bytes_at_part_req = self.rtt_rxd_bytes
if self.req_data_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
@@ -957,6 +1007,18 @@ class Resource:
except Exception as e:
RNS.log("Error while executing callbacks on resource cancel from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def _rejected(self):
if self.status < Resource.COMPLETE:
if self.initiator:
self.status = Resource.REJECTED
self.link.cancel_outgoing_resource(self)
if self.callback != None:
try:
self.link.resource_concluded(self)
self.callback(self)
except Exception as e:
RNS.log("Error while executing callbacks on resource reject from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def set_callback(self, callback):
self.callback = callback
@@ -981,7 +1043,7 @@ class Resource:
processed_segments = self.segment_index-1
current_segment_parts = self.total_parts
max_parts_per_segment = math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU)
max_parts_per_segment = math.ceil(Resource.MAX_EFFICIENT_SIZE/self.sdu)
previously_processed_parts = processed_segments*max_parts_per_segment
@@ -1004,7 +1066,7 @@ class Resource:
processed_segments = self.segment_index-1
current_segment_parts = self.total_parts
max_parts_per_segment = math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU)
max_parts_per_segment = math.ceil(Resource.MAX_EFFICIENT_SIZE/self.sdu)
previously_processed_parts = processed_segments*max_parts_per_segment
+69 -9
View File
@@ -34,9 +34,9 @@ if get_platform() == "android":
from .Interfaces.Android import SerialInterface
from .Interfaces.Android import KISSInterface
else:
from .Interfaces import *
from RNS.Interfaces import *
from .vendor.configobj import ConfigObj
from RNS.vendor.configobj import ConfigObj
import configparser
import multiprocessing.connection
import signal
@@ -89,6 +89,16 @@ class Reticulum:
the default value.
"""
LINK_MTU_DISCOVERY = False
"""
Whether automatic link MTU discovery is enabled by default in this
release. Link MTU discovery significantly increases throughput over
fast links, but requires all intermediary hops to also support it.
Support for this feature was added in RNS version 0.9.0. This option
will become enabled by default in the near future. Please update your
RNS instances.
"""
MAX_QUEUED_ANNOUNCES = 16384
QUEUED_ANNOUNCE_LIFE = 60*60*24
@@ -151,32 +161,34 @@ class Reticulum:
interfacepath = ""
__instance = None
__interface_detach_ran = False
@staticmethod
def exit_handler():
# This exit handler is called whenever Reticulum is asked to
# shut down, and will in turn call exit handlers in other
# classes, saving necessary information to disk and carrying
# out cleanup operations.
if not Reticulum.__interface_detach_ran:
RNS.Transport.detach_interfaces()
RNS.Transport.exit_handler()
RNS.Identity.exit_handler()
if RNS.profiler_ran:
RNS.profiler_results()
if RNS.Profiler.ran():
RNS.Profiler.results()
@staticmethod
def sigint_handler(signal, frame):
RNS.Transport.detach_interfaces()
Reticulum.__interface_detach_ran = True
RNS.exit()
@staticmethod
def sigterm_handler(signal, frame):
RNS.Transport.detach_interfaces()
Reticulum.__interface_detach_ran = True
RNS.exit()
@staticmethod
def get_instance():
"""
@@ -213,6 +225,9 @@ class Reticulum:
if logdest == RNS.LOG_FILE:
RNS.logdest = RNS.LOG_FILE
RNS.logfile = Reticulum.configdir+"/logfile"
elif callable(logdest):
RNS.logdest = RNS.LOG_CALLBACK
RNS.logcall = logdest
Reticulum.configpath = Reticulum.configdir+"/config"
Reticulum.storagepath = Reticulum.configdir+"/storage"
@@ -222,6 +237,7 @@ class Reticulum:
Reticulum.interfacepath = Reticulum.configdir+"/interfaces"
Reticulum.__transport_enabled = False
Reticulum.__link_mtu_discovery = Reticulum.LINK_MTU_DISCOVERY
Reticulum.__remote_management_enabled = False
Reticulum.__use_implicit_proof = True
Reticulum.__allow_probes = False
@@ -247,6 +263,7 @@ class Reticulum:
RNS.loglevel = self.requested_loglevel
self.is_shared_instance = False
self.shared_instance_interface = None
self.require_shared = require_shared_instance
self.is_connected_to_shared_instance = False
self.is_standalone_instance = False
@@ -283,7 +300,8 @@ class Reticulum:
time.sleep(1.5)
self.__apply_config()
RNS.log("Configuration loaded from "+self.configpath, RNS.LOG_VERBOSE)
RNS.log(f"Utilising cryptography backend \"{RNS.Cryptography.Provider.backend()}\"", RNS.LOG_VERBOSE)
RNS.log(f"Configuration loaded from {self.configpath}", RNS.LOG_VERBOSE)
RNS.Identity.load_known_destinations()
@@ -335,6 +353,7 @@ class Reticulum:
interface.bitrate = Reticulum._force_shared_instance_bitrate
interface._force_bitrate = Reticulum._force_shared_instance_bitrate
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
interface.optimise_mtu()
if self.require_shared == True:
interface.detach()
@@ -343,6 +362,7 @@ class Reticulum:
else:
RNS.Transport.interfaces.append(interface)
self.shared_instance_interface = interface
self.is_shared_instance = True
RNS.log("Started shared instance interface: "+str(interface), RNS.LOG_DEBUG)
self.__start_jobs()
@@ -359,6 +379,7 @@ class Reticulum:
interface.bitrate = Reticulum._force_shared_instance_bitrate
interface._force_bitrate = True
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
interface.optimise_mtu()
RNS.Transport.interfaces.append(interface)
self.is_shared_instance = False
self.is_standalone_instance = False
@@ -419,6 +440,10 @@ class Reticulum:
v = self.config["reticulum"].as_bool(option)
if v == True:
Reticulum.__transport_enabled = True
if option == "link_mtu_discovery":
v = self.config["reticulum"].as_bool(option)
if v == True:
Reticulum.__link_mtu_discovery = True
if option == "enable_remote_management":
v = self.config["reticulum"].as_bool(option)
if v == True:
@@ -576,6 +601,7 @@ class Reticulum:
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
interface.optimise_mtu()
if ifac_size != None:
interface.ifac_size = ifac_size
else:
@@ -738,6 +764,7 @@ class Reticulum:
if configured_bitrate:
interface.bitrate = configured_bitrate
interface.optimise_mtu()
if ifac_size != None:
interface.ifac_size = ifac_size
@@ -935,6 +962,9 @@ class Reticulum:
if hasattr(interface, "r_channel_load_long"):
ifstats["channel_load_long"] = interface.r_channel_load_long
if hasattr(interface, "r_noise_floor"):
ifstats["noise_floor"] = interface.r_noise_floor
if hasattr(interface, "r_battery_state"):
if interface.r_battery_state != 0x00:
ifstats["battery_state"] = interface.get_battery_state_string()
@@ -948,6 +978,18 @@ class Reticulum:
else:
ifstats["bitrate"] = None
if hasattr(interface, "current_rx_speed"):
if interface.current_rx_speed != None:
ifstats["rxs"] = interface.current_rx_speed
else:
ifstats["rxs"] = 0
if hasattr(interface, "current_tx_speed"):
if interface.current_tx_speed != None:
ifstats["txs"] = interface.current_tx_speed
else:
ifstats["txs"] = 0
if hasattr(interface, "peers"):
if interface.peers != None:
ifstats["peers"] = len(interface.peers)
@@ -985,6 +1027,10 @@ class Reticulum:
stats = {}
stats["interfaces"] = interfaces
stats["rxb"] = RNS.Transport.traffic_rxb
stats["txb"] = RNS.Transport.traffic_txb
stats["rxs"] = RNS.Transport.speed_rx
stats["txs"] = RNS.Transport.speed_tx
if Reticulum.transport_enabled():
stats["transport_id"] = RNS.Transport.identity.hash
stats["transport_uptime"] = time.time()-RNS.Transport.start_time
@@ -1205,6 +1251,20 @@ class Reticulum:
"""
return Reticulum.__transport_enabled
@staticmethod
def link_mtu_discovery():
"""
Returns whether link MTU discovery is enabled for the running
instance.
When link MTU discovery is enabled, Reticulum will
automatically upgrade link MTUs to the highest supported
value, increasing transfer speed and efficiency.
:returns: True if link MTU discovery is enabled, False if not.
"""
return Reticulum.__link_mtu_discovery
@staticmethod
def remote_management_enabled():
"""
+175 -44
View File
@@ -25,6 +25,7 @@ import RNS
import time
import math
import struct
import inspect
import threading
from time import sleep
from .vendor import umsgpack as umsgpack
@@ -84,7 +85,8 @@ class Transport:
destinations = [] # All active destinations
pending_links = [] # Links that are being established
active_links = [] # Links that are active
packet_hashlist = [] # A list of packet hashes for duplicate detection
packet_hashlist = set() # A list of packet hashes for duplicate detection
packet_hashlist_prev = set()
receipts = [] # Receipts of all outgoing packets for proof processing
# TODO: "destination_table" should really be renamed to "path_table"
@@ -102,9 +104,9 @@ class Transport:
path_requests = {} # A table for storing path request timestamps
path_states = {} # A table for keeping track of path states
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
discovery_pr_tags = [] # A table for keeping track of tagged path requests
max_pr_tags = 32000 # Maximum amount of unique path request tags to remember
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
discovery_pr_tags = [] # A table for keeping track of tagged path requests
max_pr_tags = 32000 # Maximum amount of unique path request tags to remember
# Transport control destinations are used
# for control purposes like path requests
@@ -140,6 +142,12 @@ class Transport:
interface_last_jobs = 0.0
interface_jobs_interval = 5.0
traffic_rxb = 0
traffic_txb = 0
speed_rx = 0
speed_tx = 0
traffic_captured = None
identity = None
@staticmethod
@@ -164,7 +172,8 @@ class Transport:
if os.path.isfile(packet_hashlist_path):
try:
file = open(packet_hashlist_path, "rb")
Transport.packet_hashlist = umsgpack.unpackb(file.read())
hashlist_data = umsgpack.unpackb(file.read())
Transport.packet_hashlist = set(hashlist_data)
file.close()
except Exception as e:
RNS.log("Could not load packet hashlist from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -192,6 +201,9 @@ class Transport:
thread = threading.Thread(target=Transport.jobloop, daemon=True)
thread.start()
thread = threading.Thread(target=Transport.count_traffic_loop, daemon=True)
thread.start()
if RNS.Reticulum.transport_enabled():
destination_table_path = RNS.Reticulum.storagepath+"/destination_table"
tunnel_table_path = RNS.Reticulum.storagepath+"/tunnels"
@@ -318,6 +330,40 @@ class Transport:
except Exception as e:
RNS.log(f"Could not prioritize interfaces according to bitrate. The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod
def count_traffic_loop():
while True:
time.sleep(1)
try:
rxb = 0; txb = 0;
rxs = 0; txs = 0;
for interface in Transport.interfaces:
if not hasattr(interface, "parent_interface") or interface.parent_interface == None:
if hasattr(interface, "transport_traffic_counter"):
now = time.time(); irxb = interface.rxb; itxb = interface.txb
tc = interface.transport_traffic_counter
rx_diff = irxb - tc["rxb"]
tx_diff = itxb - tc["txb"]
ts_diff = now - tc["ts"]
rxb += rx_diff; crxs = (rx_diff*8)/ts_diff
txb += tx_diff; ctxs = (tx_diff*8)/ts_diff
interface.current_rx_speed = crxs; rxs += crxs
interface.current_tx_speed = ctxs; txs += ctxs
tc["rxb"] = irxb;
tc["txb"] = itxb;
tc["ts"] = now;
else:
interface.transport_traffic_counter = {"ts": time.time(), "rxb": interface.rxb, "txb": interface.txb}
Transport.traffic_rxb += rxb
Transport.traffic_txb += txb
Transport.speed_rx = rxs
Transport.speed_tx = txs
except Exception as e:
RNS.log(f"An error occurred while counting interface traffic: {e}", RNS.LOG_ERROR)
@staticmethod
def jobloop():
while (True):
@@ -445,8 +491,9 @@ class Transport:
# Cull the packet hashlist if it has reached its max size
if len(Transport.packet_hashlist) > Transport.hashlist_maxsize:
Transport.packet_hashlist = Transport.packet_hashlist[len(Transport.packet_hashlist)-Transport.hashlist_maxsize:len(Transport.packet_hashlist)-1]
if len(Transport.packet_hashlist) > Transport.hashlist_maxsize//2:
Transport.packet_hashlist_prev = Transport.packet_hashlist
Transport.packet_hashlist = set()
# Cull the path request tags list if it has reached its max size
if len(Transport.discovery_pr_tags) > Transport.max_pr_tags:
@@ -982,10 +1029,10 @@ class Transport:
else:
pass
if should_transmit:
if not stored_hash:
Transport.packet_hashlist.append(packet.packet_hash)
Transport.packet_hashlist.add(packet.packet_hash)
stored_hash = True
# TODO: Re-evaluate potential for blocking
@@ -1051,7 +1098,7 @@ class Transport:
RNS.log("Dropped invalid GROUP announce packet", RNS.LOG_DEBUG)
return False
if not packet.packet_hash in Transport.packet_hashlist:
if not packet.packet_hash in Transport.packet_hashlist and not packet.packet_hash in Transport.packet_hashlist_prev:
return True
else:
if packet.packet_type == RNS.Packet.ANNOUNCE:
@@ -1149,29 +1196,23 @@ class Transport:
if hasattr(interface, "r_stat_rssi"):
if interface.r_stat_rssi != None:
packet.rssi = interface.r_stat_rssi
if len(Transport.local_client_interfaces) > 0:
Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi])
while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_rssi_cache.pop(0)
Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi])
while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_rssi_cache.pop(0)
if hasattr(interface, "r_stat_snr"):
if interface.r_stat_rssi != None:
packet.snr = interface.r_stat_snr
if len(Transport.local_client_interfaces) > 0:
Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr])
while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_snr_cache.pop(0)
Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr])
while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_snr_cache.pop(0)
if hasattr(interface, "r_stat_q"):
if interface.r_stat_q != None:
packet.q = interface.r_stat_q
if len(Transport.local_client_interfaces) > 0:
Transport.local_client_q_cache.append([packet.packet_hash, packet.q])
while len(Transport.local_client_q_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_q_cache.pop(0)
Transport.local_client_q_cache.append([packet.packet_hash, packet.q])
while len(Transport.local_client_q_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_q_cache.pop(0)
if len(Transport.local_client_interfaces) > 0:
if Transport.is_local_client_interface(interface):
@@ -1202,7 +1243,7 @@ class Transport:
remember_packet_hash = False
if remember_packet_hash:
Transport.packet_hashlist.append(packet.packet_hash)
Transport.packet_hashlist.add(packet.packet_hash)
# TODO: Enable when caching has been redesigned
# Transport.cache(packet)
@@ -1287,6 +1328,24 @@ class Transport:
now = time.time()
proof_timeout = Transport.extra_link_proof_timeout(packet.receiving_interface)
proof_timeout += now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops)
path_mtu = RNS.Link.mtu_from_lr_packet(packet)
nh_mtu = outbound_interface.HW_MTU
if path_mtu:
if outbound_interface.HW_MTU == None:
RNS.log(f"No next-hop HW MTU, disabling link MTU upgrade", RNS.LOG_DEBUG) # TODO: Remove debug
path_mtu = None
new_raw = new_raw[:-RNS.Link.LINK_MTU_SIZE]
elif not outbound_interface.AUTOCONFIGURE_MTU:
RNS.log(f"Outbound interface doesn't support MTU autoconfiguration, disabling link MTU upgrade", RNS.LOG_DEBUG) # TODO: Remove debug
path_mtu = None
new_raw = new_raw[:-RNS.Link.LINK_MTU_SIZE]
else:
if nh_mtu < path_mtu:
path_mtu = nh_mtu
clamped_mtu = RNS.Link.mtu_bytes(path_mtu)
RNS.log(f"Clamping link MTU to {RNS.prettysize(nh_mtu)}: {RNS.hexrep(clamped_mtu)}", RNS.LOG_DEBUG) # TODO: Remove debug
new_raw = new_raw[:-RNS.Link.LINK_MTU_SIZE]+clamped_mtu
# Entry format is
link_entry = [ now, # 0: Timestamp,
@@ -1299,7 +1358,7 @@ class Transport:
False, # 7: Validated
proof_timeout] # 8: Proof timeout timestamp
Transport.link_table[packet.getTruncatedHash()] = link_entry
Transport.link_table[RNS.Link.link_id_from_lr_packet(packet)] = link_entry
else:
# Entry format is
@@ -1349,7 +1408,7 @@ class Transport:
# Add this packet to the filter hashlist if we
# have determined that it's actually our turn
# to process it.
Transport.packet_hashlist.append(packet.packet_hash)
Transport.packet_hashlist.add(packet.packet_hash)
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
@@ -1402,7 +1461,8 @@ class Transport:
now = time.time()
if now < announce_entry[1]:
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)
if packet.destination_hash in Transport.announce_table:
Transport.announce_table.pop(packet.destination_hash)
else:
received_from = packet.destination_hash
@@ -1713,11 +1773,20 @@ class Transport:
execute_callback = False
if execute_callback:
handler.received_announce(
destination_hash=packet.destination_hash,
announced_identity=announce_identity,
app_data=RNS.Identity.recall_app_data(packet.destination_hash)
)
if len(inspect.signature(handler.received_announce).parameters) == 3:
handler.received_announce(destination_hash=packet.destination_hash,
announced_identity=announce_identity,
app_data=RNS.Identity.recall_app_data(packet.destination_hash))
elif len(inspect.signature(handler.received_announce).parameters) == 4:
handler.received_announce(destination_hash=packet.destination_hash,
announced_identity=announce_identity,
app_data=RNS.Identity.recall_app_data(packet.destination_hash),
announce_packet_hash = packet.packet_hash)
else:
raise TypeError("Invalid signature for announce handler callback")
except Exception as e:
RNS.log("Error while processing external announce callback.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -1728,6 +1797,24 @@ class Transport:
if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
for destination in Transport.destinations:
if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
path_mtu = RNS.Link.mtu_from_lr_packet(packet)
if packet.receiving_interface.AUTOCONFIGURE_MTU:
nh_mtu = packet.receiving_interface.HW_MTU
else:
nh_mtu = RNS.Reticulum.MTU
if path_mtu:
if packet.receiving_interface.HW_MTU == None:
RNS.log(f"No next-hop HW MTU, disabling link MTU upgrade", RNS.LOG_DEBUG) # TODO: Remove debug
path_mtu = None
packet.data = packet.data[:-RNS.Link.LINK_MTU_SIZE]
else:
if nh_mtu < path_mtu:
path_mtu = nh_mtu
clamped_mtu = RNS.Link.mtu_bytes(path_mtu)
RNS.log(f"Clamping link MTU to {RNS.prettysize(nh_mtu)}", RNS.LOG_DEBUG) # TODO: Remove debug
packet.data = packet.data[:-RNS.Link.LINK_MTU_SIZE]+clamped_mtu
packet.destination = destination
destination.receive(packet)
@@ -1785,12 +1872,16 @@ class Transport:
if packet.hops == link_entry[3]:
if packet.receiving_interface == link_entry[2]:
try:
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2 or len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2+RNS.Link.LINK_MTU_SIZE:
mtu_bytes = b""
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2+RNS.Link.LINK_MTU_SIZE:
mtu_bytes = RNS.Link.mtu_bytes(RNS.Link.mtu_from_lp_packet(packet))
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
peer_identity = RNS.Identity.recall(link_entry[6])
peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes+mtu_bytes
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if peer_identity.validate(signature, signed_data):
@@ -1832,7 +1923,7 @@ class Transport:
# Add this packet to the filter hashlist if we
# have determined that it's actually destined
# for this system, and then validate the proof
Transport.packet_hashlist.append(packet.packet_hash)
Transport.packet_hashlist.add(packet.packet_hash)
link.validate_proof(packet)
elif packet.context == RNS.Packet.RESOURCE_PRF:
@@ -2021,8 +2112,9 @@ class Transport:
Registers an announce handler.
:param handler: Must be an object with an *aspect_filter* attribute and a *received_announce(destination_hash, announced_identity, app_data)*
callable. Can optionally have a *receive_path_responses* attribute set to ``True``, to also receive all path responses, in addition to live
announces. See the :ref:`Announce Example<example-announce>` for more info.
or *received_announce(destination_hash, announced_identity, app_data, announce_packet_hash)* callable. Can optionally have a
*receive_path_responses* attribute set to ``True``, to also receive all path responses, in addition to live announces. See
the :ref:`Announce Example<example-announce>` for more info.
"""
if hasattr(handler, "received_announce") and callable(handler.received_announce):
if hasattr(handler, "aspect_filter"):
@@ -2183,6 +2275,17 @@ class Transport:
else:
return None
@staticmethod
def next_hop_interface_hw_mtu(destination_hash):
next_hop_interface = Transport.next_hop_interface(destination_hash)
if next_hop_interface != None:
if next_hop_interface.AUTOCONFIGURE_MTU:
return next_hop_interface.HW_MTU
else:
return None
else:
return None
@staticmethod
def next_hop_per_bit_latency(destination_hash):
next_hop_interface_bitrate = Transport.next_hop_interface_bitrate(destination_hash)
@@ -2559,7 +2662,7 @@ class Transport:
# Currently no rules are being applied
# here, and all interfaces will be sent
# the detach call on RNS teardown.
if True:
if not interface.detached:
detachable_interfaces.append(interface)
else:
pass
@@ -2568,17 +2671,45 @@ class Transport:
# Currently no rules are being applied
# here, and all interfaces will be sent
# the detach call on RNS teardown.
if True:
if not interface.detached:
detachable_interfaces.append(interface)
else:
pass
shared_instance_master = None
local_interfaces = []
detach_threads = []
RNS.log("Detaching interfaces", RNS.LOG_DEBUG)
for interface in detachable_interfaces:
try:
interface.detach()
if type(interface) == RNS.Interfaces.LocalInterface.LocalServerInterface:
shared_instance_master = interface
elif type(interface) == RNS.Interfaces.LocalInterface.LocalClientInterface:
local_interfaces.append(interface)
else:
def detach_job():
RNS.log(f"Detaching {interface}", RNS.LOG_EXTREME)
interface.detach()
dt = threading.Thread(target=detach_job, daemon=False)
dt.start()
detach_threads.append(dt)
except Exception as e:
RNS.log("An error occurred while detaching "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
for dt in detach_threads:
dt.join()
RNS.log("Detaching local clients", RNS.LOG_DEBUG)
for li in local_interfaces:
li.detach()
RNS.log("Detaching shared instance", RNS.LOG_DEBUG)
if shared_instance_master != None:
shared_instance_master.detach()
RNS.log("All interfaces detached", RNS.LOG_DEBUG)
@staticmethod
def shared_connection_disappeared():
for link in Transport.active_links:
@@ -2645,13 +2776,13 @@ class Transport:
save_start = time.time()
if not RNS.Reticulum.transport_enabled():
Transport.packet_hashlist = []
Transport.packet_hashlist = set()
else:
RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG)
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
file = open(packet_hashlist_path, "wb")
file.write(umsgpack.packb(Transport.packet_hashlist))
file.write(umsgpack.packb(list(Transport.packet_hashlist)))
file.close()
save_time = time.time() - save_start
+4 -2
View File
@@ -23,5 +23,7 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
py_modules = glob.glob(os.path.dirname(__file__)+"/*.py")
pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
modules = py_modules+pyc_modules
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
+18 -5
View File
@@ -294,9 +294,10 @@ current_resource = None
stats = []
speed = 0.0
phy_speed = 0.0
phy_got_total = 0
def sender_progress(resource):
stats_max = 32
global current_resource, stats, speed, phy_speed, resource_done
global current_resource, stats, speed, phy_speed, phy_got_total, resource_done
current_resource = resource
now = time.time()
@@ -321,6 +322,7 @@ def sender_progress(resource):
phy_diff = phy_got - stats[0][2]
if phy_diff > 0:
phy_speed = phy_diff/span
# phy_got_total += phy_diff
if resource.status < RNS.Resource.COMPLETE:
resource_done = False
@@ -581,8 +583,8 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(0)
def send(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False):
global current_resource, resource_done, link, speed, show_phy_rates
def send(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False, no_compress=False):
global current_resource, resource_done, link, speed, show_phy_rates, phy_got_total, phy_speed
from tempfile import TemporaryFile
targetloglevel = 3+verbosity-quietness
show_phy_rates = phy_rates
@@ -705,7 +707,10 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
print(f"{erase_str}Advertising file resource ", end=es)
link.identify(identity)
resource = RNS.Resource(temp_file, link, callback = sender_progress, progress_callback = sender_progress)
auto_compress = True
if no_compress:
auto_compress = False
resource = RNS.Resource(temp_file, link, callback = sender_progress, progress_callback = sender_progress, auto_compress = auto_compress)
current_resource = resource
while resource.status < RNS.Resource.TRANSFERRING:
@@ -715,6 +720,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
sys.stdout.flush()
i = (i+1)%len(syms)
resource_started_at = time.time()
if resource.status > RNS.Resource.COMPLETE:
if silent:
@@ -732,7 +738,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
time.sleep(0.1)
prg = current_resource.get_progress()
percent = round(prg * 100.0, 1)
if show_phy_rates:
if show_phy_rates and not resource_done:
pss = size_str(phy_speed, "b")
phy_str = f" ({pss}ps at physical layer)"
else:
@@ -754,6 +760,11 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
if not silent:
i = progress_update(i)
resource_concluded_at = time.time()
transfer_time = resource_concluded_at - resource_started_at
speed = current_resource.total_size/transfer_time
# phy_speed = phy_got_total/transfer_time
if not silent:
i = progress_update(i, done=True)
@@ -784,6 +795,7 @@ def main():
parser.add_argument('-q', '--quiet', action='count', default=0, help="decrease verbosity")
parser.add_argument("-S", '--silent', action='store_true', default=False, help="disable transfer progress output")
parser.add_argument("-l", '--listen', action='store_true', default=False, help="listen for incoming transfer requests")
parser.add_argument("-C", '--no-compress', action='store_true', default=False, help="disable automatic compression")
parser.add_argument("-F", '--allow-fetch', action='store_true', default=False, help="allow authenticated clients to fetch files")
parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending")
parser.add_argument("-j", "--jail", metavar="path", action="store", default=None, help="restrict fetch requests to specified path", type=str)
@@ -842,6 +854,7 @@ def main():
timeout = args.w,
silent = args.silent,
phy_rates = args.phy_rates,
no_compress = args.no_compress,
)
else:
+293 -93
View File
@@ -2,7 +2,7 @@
# MIT License
#
# Copyright (c) 2018-2022 Mark Qvist - unsigned.io/rnode
# Copyright (c) 2018-2025 Mark Qvist
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -41,7 +41,7 @@ import RNS
RNS.logtimefmt = "%H:%M:%S"
RNS.compact_log_fmt = True
program_version = "2.2.0"
program_version = "2.4.0"
eth_addr = "0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73"
btc_addr = "35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH"
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
@@ -84,8 +84,11 @@ class KISS():
CMD_NP_INT = 0x65
CMD_DISP_ADR = 0x63
CMD_DISP_BLNK = 0x64
CMD_DISP_ROT = 0x67
CMD_DISP_RCND = 0x68
CMD_BT_CTRL = 0x46
CMD_BT_PIN = 0x62
CMD_DIS_IA = 0x69
CMD_BOARD = 0x47
CMD_PLATFORM = 0x48
CMD_MCU = 0x49
@@ -121,76 +124,80 @@ class KISS():
return data
class ROM():
PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80
PLATFORM_NRF52 = 0x70
PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80
PLATFORM_NRF52 = 0x70
MCU_1284P = 0x91
MCU_2560 = 0x92
MCU_ESP32 = 0x81
MCU_NRF52 = 0x71
MCU_1284P = 0x91
MCU_2560 = 0x92
MCU_ESP32 = 0x81
MCU_NRF52 = 0x71
PRODUCT_RNODE = 0x03
MODEL_A1 = 0xA1
MODEL_A6 = 0xA6
MODEL_A4 = 0xA4
MODEL_A9 = 0xA9
MODEL_A3 = 0xA3
MODEL_A8 = 0xA8
MODEL_A2 = 0xA2
MODEL_A7 = 0xA7
MODEL_A5 = 0xA5
MODEL_AA = 0xAA
PRODUCT_RNODE = 0x03
MODEL_A1 = 0xA1
MODEL_A6 = 0xA6
MODEL_A4 = 0xA4
MODEL_A9 = 0xA9
MODEL_A3 = 0xA3
MODEL_A8 = 0xA8
MODEL_A2 = 0xA2
MODEL_A7 = 0xA7
MODEL_A5 = 0xA5
MODEL_AA = 0xAA
MODEL_AC = 0xAC
PRODUCT_T32_10 = 0xB2
MODEL_BA = 0xBA
MODEL_BB = 0xBB
PRODUCT_T32_10 = 0xB2
MODEL_BA = 0xBA
MODEL_BB = 0xBB
PRODUCT_T32_20 = 0xB0
MODEL_B3 = 0xB3
MODEL_B8 = 0xB8
PRODUCT_T32_20 = 0xB0
MODEL_B3 = 0xB3
MODEL_B8 = 0xB8
PRODUCT_T32_21 = 0xB1
MODEL_B4 = 0xB4
MODEL_B9 = 0xB9
MODEL_B4_TCXO = 0x04 # The TCXO model codes are only used here to select the
MODEL_B9_TCXO = 0x09 # correct firmware, actual model codes in firmware is
# still 0xB4 and 0xB9.
PRODUCT_T32_21 = 0xB1
MODEL_B4 = 0xB4
MODEL_B9 = 0xB9
MODEL_B4_TCXO = 0x04 # The TCXO model codes are only used here to select the correct firmware,
MODEL_B9_TCXO = 0x09 # actual model codes in firmware is still 0xB4 and 0xB9.
PRODUCT_H32_V2 = 0xC0
MODEL_C4 = 0xC4
MODEL_C9 = 0xC9
PRODUCT_H32_V2 = 0xC0
MODEL_C4 = 0xC4
MODEL_C9 = 0xC9
PRODUCT_H32_V3 = 0xC1
MODEL_C5 = 0xC5
MODEL_CA = 0xCA
PRODUCT_H32_V3 = 0xC1
MODEL_C5 = 0xC5
MODEL_CA = 0xCA
PRODUCT_TBEAM = 0xE0
MODEL_E4 = 0xE4
MODEL_E9 = 0xE9
MODEL_E3 = 0xE3
MODEL_E8 = 0xE8
PRODUCT_TBEAM = 0xE0
MODEL_E4 = 0xE4
MODEL_E9 = 0xE9
MODEL_E3 = 0xE3
MODEL_E8 = 0xE8
PRODUCT_TBEAM_S_V1 = 0xEA
MODEL_DB = 0xDB
MODEL_DC = 0xDC
PRODUCT_TBEAM_S_V1= 0xEA
MODEL_DB = 0xDB
MODEL_DC = 0xDC
PRODUCT_TDECK = 0xD0
MODEL_D4 = 0xD4
MODEL_D9 = 0xD9
PRODUCT_TDECK = 0xD0
MODEL_D4 = 0xD4
MODEL_D9 = 0xD9
PRODUCT_RAK4631 = 0x10
MODEL_11 = 0x11
MODEL_12 = 0x12
MODEL_13 = 0x13
MODEL_14 = 0x14
PRODUCT_RAK4631 = 0x10
MODEL_11 = 0x11
MODEL_12 = 0x12
MODEL_13 = 0x13
MODEL_14 = 0x14
PRODUCT_OPENCOM_XL = 0x20
MODEL_21 = 0x21
PRODUCT_OPENCOM_XL = 0x20
MODEL_21 = 0x21
PRODUCT_TECHO = 0x15
MODEL_16 = 0x16
MODEL_17 = 0x17
PRODUCT_TECHO = 0x15
MODEL_T4 = 0x16
MODEL_T9 = 0x17
PRODUCT_HELTEC_T114 = 0xC2
BOARD_HELTEC_T114 = 0x3C
MODEL_C6 = 0xC6 # Heltec Mesh Node T114, 470-510 MHz (HT-n5262-LF)
MODEL_C7 = 0xC7 # Heltec Mesh Node T114, 863-928 MHz (HT-n5262-HF)
PRODUCT_HMBRW = 0xF0
MODEL_FF = 0xFF
@@ -211,6 +218,17 @@ class ROM():
ADDR_CONF_FREQ = 0xA3
ADDR_CONF_OK = 0xA7
ADDR_CONF_BT = 0xB0
ADDR_CONF_DSET = 0xB1
ADDR_CONF_DINT = 0xB2
ADDR_CONF_DADR = 0xB3
ADDR_CONF_DBLK = 0xB4
ADDR_CONF_DROT = 0xB8
ADDR_CONF_PSET = 0xB5
ADDR_CONF_PINT = 0xB6
ADDR_CONF_BSET = 0xB7
ADDR_CONF_DIA = 0xB9
INFO_LOCK_BYTE = 0x73
CONF_OK_BYTE = 0x73
@@ -242,6 +260,7 @@ products = {
ROM.PRODUCT_TECHO: "LilyGO T-Echo",
ROM.PRODUCT_RAK4631: "RAK4631",
ROM.PRODUCT_OPENCOM_XL: "openCom XL",
ROM.PRODUCT_HELTEC_T114: "Heltec Mesh Node T114",
}
platforms = {
@@ -264,6 +283,7 @@ models = {
0xA6: [820000000, 1020000000, 22, "820 - 960 MHz", "rnode_firmware_t3s3.zip", "SX1262"],
0xA5: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_t3s3_sx127x.zip", "SX1278"],
0xAA: [820000000, 1020000000, 17, "820 - 960 MHz", "rnode_firmware_t3s3_sx127x.zip", "SX1276"],
0xAC: [2400000000, 2500000000, 20, "2.4 - 2.5 GHz", "rnode_firmware_t3s3_sx1280_pa.zip", "SX1280"],
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip", "SX1278"],
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip", "SX1276"],
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip", "SX1278"],
@@ -278,8 +298,10 @@ models = {
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip", "SX1276"],
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip", "SX1278"],
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip", "SX1276"],
0xC5: [420000000, 520000000, 21, "420 - 520 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
0xCA: [850000000, 950000000, 21, "850 - 950 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
0xC5: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_heltec32v3.zip", "SX1268"],
0xCA: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
0xC6: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_heltec_t114.zip", "SX1268"],
0xC7: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_heltec_t114.zip", "SX1262"],
0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip", "SX1278"],
0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip", "SX1276"],
0xD4: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tdeck.zip", "SX1268"],
@@ -665,6 +687,30 @@ class RNode():
if written != len(kiss_command):
raise IOError("An IO error occurred while sending display blanking timeout command to device")
def set_display_rotation(self, rotation):
data = bytes([rotation & 0xFF])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_ROT])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending display rotation command to device")
def recondition_display(self):
data = bytes([0x01])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_RCND])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending display recondition command to device")
def set_disable_interference_avoidance(self, ia_disabled):
if ia_disabled:
data = bytes([0x01])
else:
data = bytes([0x00])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DIS_IA])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending interference avoidance configuration command to device")
def set_neopixel_intensity(self, intensity):
data = bytes([intensity & 0xFF])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_NP_INT])+data+bytes([KISS.FEND])
@@ -1148,9 +1194,10 @@ def ensure_firmware_file(fw_filename):
RNS.log("The selected firmware for this board is version "+selected_version)
else:
RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
RNS.log("use the --fw-version option to manually specify a version.")
graceful_exit(98)
if selected_version == None:
RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
RNS.log("use the --fw-version option to manually specify a version.")
graceful_exit(98)
# if custom firmware url, use it
if fw_url != None:
@@ -1298,7 +1345,9 @@ def main():
parser.add_argument("-D", "--display", action="store", metavar="i", type=int, default=None, help="Set display intensity (0-255)")
parser.add_argument("-t", "--timeout", action="store", metavar="s", type=int, default=None, help="Set display timeout in seconds, 0 to disable")
parser.add_argument("-R", "--rotation", action="store", metavar="rotation", type=int, default=None, help="Set display rotation, valid values are 0 through 3")
parser.add_argument("--display-addr", action="store", metavar="byte", type=str, default=None, help="Set display address as hex byte (00 - FF)")
parser.add_argument("--recondition-display", action="store_true", help="Start display reconditioning")
parser.add_argument("--np", action="store", metavar="i", type=int, default=None, help="Set NeoPixel intensity (0-255)")
@@ -1308,6 +1357,11 @@ def main():
parser.add_argument("--sf", action="store", metavar="factor", type=int, default=None, help="Spreading factor for TNC mode (7 - 12)")
parser.add_argument("--cr", action="store", metavar="rate", type=int, default=None, help="Coding rate for TNC mode (5 - 8)")
parser.add_argument("-x", "--ia-enable", action="store_true", help="Enable interference avoidance")
parser.add_argument("-X", "--ia-disable", action="store_true", help="Disable interference avoidance")
parser.add_argument("-c", "--config", action="store_true", help="Print device configuration")
parser.add_argument("--eeprom-backup", action="store_true", help="Backup EEPROM to file")
parser.add_argument("--eeprom-dump", action="store_true", help="Dump EEPROM to console")
parser.add_argument("--eeprom-wipe", action="store_true", help="Unlock and wipe EEPROM")
@@ -1321,7 +1375,7 @@ def main():
parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") #
parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash")
parser.add_argument("-H", "--firmware-hash", action="store", help="Set installed firmware hash")
parser.add_argument("-K", "--get-target-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get target firmware hash from device
parser.add_argument("-L", "--get-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get calculated firmware hash from device
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap")
@@ -1641,30 +1695,31 @@ def main():
print("")
print("What kind of device is this?\n")
print("[1] A specific kind of RNode")
print(" .")
print(" / \\ Select this option if you have an RNode of a specific")
print(" | type, built from a recipe or bought from a vendor.")
print(" | Select this option if you have an RNode of a specific")
print(" \\ / type, built from a recipe or bought from a vendor.")
print(" '")
print("[1] A specific kind of RNode")
print("")
print("[2] Homebrew RNode")
print(" .")
print(" / \\ Select this option if you have put toghether an RNode")
print(" | of your own design, or if you are prototyping one.")
print(" | Select this option if you have put toghether an RNode")
print(" \\ / of your own design, or if you are prototyping one.")
print(" '")
print("[2] Homebrew RNode")
print("")
print("[3] LilyGO LoRa32 v2.1 (aka T3 v1.6 / T3 v1.6.1)")
print("[4] LilyGO LoRa32 v2.0")
print("[5] LilyGO LoRa32 v1.0")
print("[6] LilyGO T-Beam")
print("[7] Heltec LoRa32 v2")
print("[8] Heltec LoRa32 v3")
print("[9] LilyGO LoRa T3S3")
print(" | Select one of these options if you want to easily turn")
print(" \\ / a supported development board into an RNode.")
print(" '")
print("[3] LilyGO LoRa32 v2.1 (aka T3 v1.6 / T3 v1.6.1)")
print("[4] LilyGO LoRa32 v2.0")
print("[5] LilyGO LoRa32 v1.0")
print("[6] LilyGO T-Beam")
print("[7] Heltec LoRa32 v2")
print("[8] Heltec LoRa32 v3")
print("[9] LilyGO LoRa T3S3")
print("[10] RAK4631")
print("[11] LilyGo T-Echo")
print("[12] LilyGO T-Beam Supreme")
print("[13] LilyGO T-Deck")
print(" .")
print(" / \\ Select one of these options if you want to easily turn")
print(" | a supported development board into an RNode.")
print("[14] Heltec T114")
print("")
print("---------------------------------------------------------------------------")
print("\nEnter the number that matches your device type:\n? ", end="")
@@ -1673,7 +1728,7 @@ def main():
try:
c_dev = int(input())
c_mod = False
if c_dev < 1 or c_dev > 13:
if c_dev < 1 or c_dev > 14:
raise ValueError()
elif c_dev == 1:
selected_product = ROM.PRODUCT_RNODE
@@ -1856,6 +1911,20 @@ def main():
print("who would like to experiment with it. Hit enter to continue.")
print("---------------------------------------------------------------------------")
input()
elif c_dev == 14:
selected_product = ROM.PRODUCT_HELTEC_T114
clear()
print("")
print("---------------------------------------------------------------------------")
print(" Heltec T114 RNode Installer")
print("")
print("Important! Using RNode firmware on Heltec T114 devices should currently be")
print("considered experimental. It is not intended for production or critical use.")
print("The currently supplied firmware is provided AS-IS as a courtesey to those")
print("who would like to experiment with it. Hit enter to continue.")
print("---------------------------------------------------------------------------")
input()
except Exception as e:
print("That device type does not exist, exiting now.")
graceful_exit()
@@ -1964,10 +2033,12 @@ def main():
print("");
print("[3] 433 MHz (with SX1268 chip)")
print("[4] 868/915/923 MHz (with SX1262 chip)")
print("");
print("[5] 2.4 GHz (with SX1280 chip and PA)")
print("\n? ", end="")
try:
c_model = int(input())
if c_model < 1 or c_model > 4:
if c_model < 1 or c_model > 5:
raise ValueError()
elif c_model == 1:
selected_model = ROM.MODEL_A5
@@ -1985,6 +2056,10 @@ def main():
selected_model = ROM.MODEL_A6
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 5:
selected_model = ROM.MODEL_AC
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That model does not exist, exiting now.")
graceful_exit()
@@ -2172,6 +2247,28 @@ def main():
except Exception as e:
print("That band does not exist, exiting now.")
exit()
elif selected_product == ROM.PRODUCT_HELTEC_T114:
selected_mcu = ROM.MCU_NRF52
print("\nWhat band is this Heltec T114 for?\n")
print("[1] 433 MHz")
print("[2] 868 MHz")
print("[3] 915 MHz")
print("[4] 923 MHz")
try:
c_model = int(input())
if c_model < 1 or c_model > 4:
raise ValueError()
elif c_model == 1:
selected_model = ROM.MODEL_C6
selected_platform = ROM.PLATFORM_NRF52
elif c_model > 1:
selected_model = ROM.MODEL_C7
selected_platform = ROM.PLATFORM_NRF52
except Exception as e:
print("That band does not exist, exiting now.")
exit()
elif selected_product == ROM.PRODUCT_RAK4631:
selected_mcu = ROM.MCU_NRF52
print("\nWhat band is this RAK4631 for?\n")
@@ -2203,13 +2300,13 @@ def main():
print("\n? ", end="")
try:
c_model = int(input())
if c_model < 1 or c_model > 1:
if c_model < 1 or c_model > 4:
raise ValueError()
elif c_model == 1:
selected_model = ROM.MODEL_T4
selected_model = ROM.MODEL_16
selected_platform = ROM.PLATFORM_NRF52
elif c_model > 1:
selected_model = ROM.MODEL_T9
selected_model = ROM.MODEL_17
selected_platform = ROM.PLATFORM_NRF52
except Exception as e:
print("That band does not exist, exiting now.")
@@ -2726,6 +2823,7 @@ def main():
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.boot_app0",
"0x0", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bin",
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.partitions",
]
elif fw_filename == "rnode_firmware_featheresp32.zip":
@@ -2908,6 +3006,24 @@ def main():
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx127x.partitions",
]
elif fw_filename == "rnode_firmware_t3s3_sx1280_pa.zip":
return [
sys.executable, flasher,
"--chip", "esp32s3",
"--port", args.port,
"--baud", args.baud_flash,
"--before", "default_reset",
"--after", "hard_reset",
"write_flash", "-z",
"--flash_mode", "dio",
"--flash_freq", "80m",
"--flash_size", "4MB",
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.boot_app0",
"0x0", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.bin",
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.partitions",
]
elif fw_filename == "rnode_firmware_tbeam_supreme.zip":
return [
sys.executable, flasher,
@@ -3057,12 +3173,12 @@ def main():
if args.platform == ROM.PLATFORM_ESP32:
wants_fw_provision = True
RNS.log("Waiting for ESP32 reset...")
time.sleep(7)
time.sleep(8)
if args.platform == ROM.PLATFORM_NRF52:
wants_fw_provision = True
RNS.log("Waiting for NRF52 reset...")
# Don't need to wait as long this time.
time.sleep(5)
time.sleep(6)
else:
RNS.log("Error from flasher ("+str(flash_status)+") while writing.")
RNS.log("Some boards have trouble flashing at high speeds, and you can")
@@ -3314,6 +3430,63 @@ def main():
RNS.log("Firmware update file not found")
graceful_exit()
if args.config:
eeprom_reserved = 200
if rnode.platform == ROM.PLATFORM_ESP32:
eeprom_size = 296
elif rnode.platform == ROM.PLATFORM_NRF52:
eeprom_size = 296
else:
eeprom_size = 4096
eeprom_offset = eeprom_size-eeprom_reserved
def ea(a):
return a+eeprom_offset
ec_bt = rnode.eeprom[ROM.ADDR_CONF_BT]
ec_dint = rnode.eeprom[ROM.ADDR_CONF_DINT]
ec_dadr = rnode.eeprom[ROM.ADDR_CONF_DADR]
ec_dblk = rnode.eeprom[ROM.ADDR_CONF_DBLK]
ec_drot = rnode.eeprom[ROM.ADDR_CONF_DROT]
ec_pset = rnode.eeprom[ROM.ADDR_CONF_PSET]
ec_pint = rnode.eeprom[ROM.ADDR_CONF_PINT]
ec_bset = rnode.eeprom[ROM.ADDR_CONF_BSET]
ec_dia = rnode.eeprom[ROM.ADDR_CONF_DIA]
print("\nDevice configuration:")
if ec_bt == 0x73:
print(f" Bluetooth : Enabled")
else:
print(f" Bluetooth : Disabled")
if ec_dia == 0x00:
print(f" Interference avoidance : Enabled")
else:
print(f" Interference avoidance : Disabled")
print( f" Display brightness : {ec_dint}")
if ec_dadr == 0xFF:
print(f" Display address : Default")
else:
print(f" Display address : {RNS.hexrep(ec_dadr, delimit=False)}")
if ec_bset == 0x73 and ec_dblk != 0x00:
print(f" Display blanking : {ec_dblk}s")
else:
print(f" Display blanking : Disabled")
if ec_drot != 0xFF:
if ec_drot == 0x00:
rstr = "Landscape"
if ec_drot == 0x01:
rstr = "Portrait"
if ec_drot == 0x02:
rstr = "Landscape 180"
if ec_drot == 0x03:
rstr = "Portrait 180"
print(f" Display rotation : {rstr}")
else:
print(f" Display rotation : Default")
if ec_pset == 0x73:
print(f" Neopixel Intensity : {ec_pint}")
print("")
graceful_exit()
if args.eeprom_dump:
RNS.log("EEPROM contents:")
RNS.log(RNS.hexrep(rnode.eeprom))
@@ -3354,6 +3527,31 @@ def main():
RNS.log("Setting display timeout to "+str(di))
rnode.set_display_blanking(di)
if isinstance(args.rotation, int):
dr = args.rotation
if dr < 0:
dr = 0
if dr > 3:
dr = 3
RNS.log("Setting display rotation to "+str(dr))
rnode.set_display_rotation(dr)
if isinstance(args.recondition_display, bool):
if args.recondition_display:
RNS.log("Starting display reconditioning")
rnode.recondition_display()
if isinstance(args.ia_enable, bool):
if args.ia_enable:
RNS.log("Enabling interference avoidance")
rnode.set_disable_interference_avoidance(False)
if isinstance(args.ia_disable, bool):
if args.ia_disable:
RNS.log("Disabling interference avoidance")
rnode.set_disable_interference_avoidance(True)
if isinstance(args.np, int):
di = args.np
if di < 0:
@@ -3475,7 +3673,7 @@ def main():
elif rnode.platform == ROM.PLATFORM_NRF52:
rnode_serial.close()
RNS.log("Waiting for NRF52 reset...")
time.sleep(14)
time.sleep(18)
selected_port = None
ports = list_ports.comports()
for port in ports:
@@ -3686,7 +3884,9 @@ def main():
if rnode.platform == ROM.PLATFORM_ESP32:
rnode.hard_reset()
RNS.log("Waiting for ESP32 reset...")
time.sleep(6.5)
time.sleep(7)
if selected_model in [ROM.MODEL_AC, ROM.MODEL_A6, ROM.MODEL_A1, ROM.MODEL_AA, ROM.MODEL_A5]:
time.sleep(5)
elif rnode.platform == ROM.PLATFORM_NRF52:
# Wait a few seconds before hard resetting.
@@ -3705,7 +3905,7 @@ def main():
# Give plenty of time for to allow for
# potential e-ink display refresh too.
time.sleep(14)
time.sleep(20)
# After the hard reset, the port number will
# change. We need to find the new port number,
+2
View File
@@ -42,6 +42,8 @@ def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
if reticulum.is_connected_to_shared_instance:
RNS.log("Started rnsd version {version} connected to another shared local instance, this is probably NOT what you want!".format(version=__version__), RNS.LOG_WARNING)
else:
if RNS.Reticulum.get_instance().shared_instance_interface:
RNS.Reticulum.get_instance().shared_instance_interface.server.daemon_threads = True
RNS.log("Started rnsd version {version}".format(version=__version__), RNS.LOG_NOTICE)
while True:
+79 -12
View File
@@ -135,7 +135,7 @@ def get_remote_status(destination_hash, include_lstats, identity, no_output=Fals
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False,
lstats=False, sorting=None, sort_reverse=False, remote=None, management_identity=None,
remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT):
remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT, must_exit=True, rns_instance=None, traffic_totals=False):
if remote:
require_shared = False
@@ -143,11 +143,18 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
require_shared = True
try:
reticulum = RNS.Reticulum(configdir=configdir, loglevel=3+verbosity, require_shared_instance=require_shared)
if rns_instance:
reticulum = rns_instance
must_exit = False
else:
reticulum = RNS.Reticulum(configdir=configdir, loglevel=3+verbosity, require_shared_instance=require_shared)
except Exception as e:
print("No shared RNS instance available to get status from")
exit(1)
if must_exit:
exit(1)
else:
return
link_count = None
stats = None
@@ -175,7 +182,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
except Exception as e:
print(str(e))
exit(20)
if must_exit:
exit(20)
else:
return
else:
if lstats:
@@ -204,7 +214,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
i[k] = RNS.hexrep(i[k], delimit=False)
print(json.dumps(stats))
exit()
if must_exit:
exit()
else:
return
interfaces = stats["interfaces"]
if sorting != None and isinstance(sorting, str):
@@ -215,6 +228,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
interfaces.sort(key=lambda i: i["rxb"], reverse=not sort_reverse)
if sorting == "tx":
interfaces.sort(key=lambda i: i["txb"], reverse=not sort_reverse)
if sorting == "rxs":
interfaces.sort(key=lambda i: i["rxs"], reverse=not sort_reverse)
if sorting == "txs":
interfaces.sort(key=lambda i: i["txs"], reverse=not sort_reverse)
if sorting == "traffic":
interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse)
if sorting == "announces" or sorting == "announce":
@@ -303,6 +320,12 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "bitrate" in ifstat and ifstat["bitrate"] != None:
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
if "noise_floor" in ifstat:
if ifstat["noise_floor"] != None:
print(" Noise Fl. : {nfl} dBm".format(nfl=str(ifstat["noise_floor"])))
else:
print(" Noise Fl. : Unknown")
if "battery_percent" in ifstat and ifstat["battery_percent"] != None:
try:
bpi = int(ifstat["battery_percent"])
@@ -315,8 +338,8 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
print(" Ch. Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
@@ -348,7 +371,21 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print(" Announces : {iaf}".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
print(" {iaf}".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
print(" Traffic : {txb}\n {rxb}".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
rxb_str = ""+RNS.prettysize(ifstat["rxb"])
txb_str = ""+RNS.prettysize(ifstat["txb"])
strdiff = len(rxb_str)-len(txb_str)
if strdiff > 0:
txb_str += " "*strdiff
elif strdiff < 0:
rxb_str += " "*-strdiff
rxstat = rxb_str
txstat = txb_str
if "rxs" in ifstat and "txs" in ifstat:
rxstat += " "+RNS.prettyspeed(ifstat["rxs"])
txstat += " "+RNS.prettyspeed(ifstat["txs"])
print(f" Traffic : {txstat}\n {rxstat}")
lstr = ""
if link_count != None and lstats:
@@ -358,6 +395,19 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
else:
lstr = f" {link_count} entr{ms} in link table"
if traffic_totals:
rxb_str = ""+RNS.prettysize(stats["rxb"])
txb_str = ""+RNS.prettysize(stats["txb"])
strdiff = len(rxb_str)-len(txb_str)
if strdiff > 0:
txb_str += " "*strdiff
elif strdiff < 0:
rxb_str += " "*-strdiff
rxstat = rxb_str+" "+RNS.prettyspeed(stats["rxs"])
txstat = txb_str+" "+RNS.prettyspeed(stats["txs"])
print(f"\n Totals : {txstat}\n {rxstat}")
if "transport_id" in stats and stats["transport_id"] != None:
print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
if "probe_responder" in stats and stats["probe_responder"] != None:
@@ -375,9 +425,12 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print("Could not get RNS status")
else:
print("Could not get RNS status from remote transport instance "+RNS.prettyhexrep(identity_hash))
exit(2)
if must_exit:
exit(2)
else:
return
def main():
def main(must_exit=True, rns_instance=None):
try:
parser = argparse.ArgumentParser(description="Reticulum Network Stack Status")
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
@@ -407,11 +460,19 @@ def main():
default=False,
)
parser.add_argument(
"-t",
"--totals",
action="store_true",
help="display traffic totals",
default=False,
)
parser.add_argument(
"-s",
"--sort",
action="store",
help="sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]",
help="sort interfaces by [rate, traffic, rx, tx, rxs, txs, announces, arx, atx, held]",
default=None,
type=str
)
@@ -483,11 +544,17 @@ def main():
remote=args.R,
management_identity=args.i,
remote_timeout=args.w,
must_exit=must_exit,
rns_instance=rns_instance,
traffic_totals=args.totals,
)
except KeyboardInterrupt:
print("")
exit()
if must_exit:
exit()
else:
return
def speed_str(num, suffix='bps'):
units = ['','k','M','G','T','P','E','Z']
+206 -106
View File
@@ -24,6 +24,7 @@ import os
import sys
import glob
import time
import datetime
import random
import threading
@@ -43,8 +44,10 @@ from .Resource import Resource, ResourceAdvertisement
from .Cryptography import HKDF
from .Cryptography import Hashes
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
py_modules = glob.glob(os.path.dirname(__file__)+"/*.py")
pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
modules = py_modules+pyc_modules
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
LOG_CRITICAL = 0
LOG_ERROR = 1
@@ -57,13 +60,16 @@ LOG_EXTREME = 7
LOG_STDOUT = 0x91
LOG_FILE = 0x92
LOG_CALLBACK = 0x93
LOG_MAXSIZE = 5*1024*1024
loglevel = LOG_NOTICE
logfile = None
logdest = LOG_STDOUT
logcall = None
logtimefmt = "%Y-%m-%d %H:%M:%S"
logtimefmt_p = "%H:%M:%S.%f"
compact_log_fmt = False
instance_random = random.Random()
@@ -104,40 +110,51 @@ def timestamp_str(time_s):
timestamp = time.localtime(time_s)
return time.strftime(logtimefmt, timestamp)
def log(msg, level=3, _override_destination = False):
def precise_timestamp_str(time_s):
return datetime.datetime.now().strftime(logtimefmt_p)[:-3]
def log(msg, level=3, _override_destination = False, pt=False):
global _always_override_destination, compact_log_fmt
msg = str(msg)
if loglevel >= level:
if not compact_log_fmt:
logstring = "["+timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg
if pt:
logstring = "["+precise_timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg
else:
logstring = "["+timestamp_str(time.time())+"] "+msg
if not compact_log_fmt:
logstring = "["+timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg
else:
logstring = "["+timestamp_str(time.time())+"] "+msg
logging_lock.acquire()
with logging_lock:
if (logdest == LOG_STDOUT or _always_override_destination or _override_destination):
print(logstring)
if (logdest == LOG_STDOUT or _always_override_destination or _override_destination):
print(logstring)
logging_lock.release()
elif (logdest == LOG_FILE and logfile != None):
try:
file = open(logfile, "a")
file.write(logstring+"\n")
file.close()
if os.path.getsize(logfile) > LOG_MAXSIZE:
prevfile = logfile+".1"
if os.path.isfile(prevfile):
os.unlink(prevfile)
os.rename(logfile, prevfile)
elif (logdest == LOG_FILE and logfile != None):
try:
file = open(logfile, "a")
file.write(logstring+"\n")
file.close()
if os.path.getsize(logfile) > LOG_MAXSIZE:
prevfile = logfile+".1"
if os.path.isfile(prevfile):
os.unlink(prevfile)
os.rename(logfile, prevfile)
except Exception as e:
_always_override_destination = True
log("Exception occurred while writing log message to log file: "+str(e), LOG_CRITICAL)
log("Dumping future log events to console!", LOG_CRITICAL)
log(msg, level)
logging_lock.release()
except Exception as e:
logging_lock.release()
_always_override_destination = True
log("Exception occurred while writing log message to log file: "+str(e), LOG_CRITICAL)
log("Dumping future log events to console!", LOG_CRITICAL)
log(msg, level)
elif logdest == LOG_CALLBACK:
try:
logcall(logstring)
except Exception as e:
_always_override_destination = True
log("Exception occurred while calling external log handler: "+str(e), LOG_CRITICAL)
log("Dumping future log events to console!", LOG_CRITICAL)
log(msg, level)
def rand():
@@ -218,6 +235,11 @@ def prettydistance(m, suffix="m"):
return "%.2f %s%s" % (num, last_unit, suffix)
def prettytime(time, verbose=False, compact=False):
neg = False
if time < 0:
time = abs(time)
neg = True
days = int(time // (24 * 3600))
time = time % (24 * 3600)
hours = int(time // 3600)
@@ -268,10 +290,17 @@ def prettytime(time, verbose=False, compact=False):
if tstr == "":
return "0s"
else:
return tstr
if not neg:
return tstr
else:
return f"-{tstr}"
def prettyshorttime(time, verbose=False, compact=False):
neg = False
time = time*1e6
if time < 0:
time = abs(time)
neg = True
seconds = int(time // 1e6); time %= 1e6
milliseconds = int(time // 1e3); time %= 1e3
@@ -315,7 +344,10 @@ def prettyshorttime(time, verbose=False, compact=False):
if tstr == "":
return "0us"
else:
return tstr
if not neg:
return tstr
else:
return f"-{tstr}"
def phyparams():
print("Required Physical Layer MTU : "+str(Reticulum.MTU)+" bytes")
@@ -333,93 +365,161 @@ def exit():
print("")
sys.exit(0)
class Profiler:
_ran = False
profilers = {}
tags = {}
profiler_ran = False
profiler_tags = {}
def profiler(tag=None, capture=False, super_tag=None):
global profiler_ran, profiler_tags
try:
thread_ident = threading.get_ident()
if capture:
end = time.perf_counter()
if tag in profiler_tags and thread_ident in profiler_tags[tag]["threads"]:
if profiler_tags[tag]["threads"][thread_ident]["current_start"] != None:
begin = profiler_tags[tag]["threads"][thread_ident]["current_start"]
profiler_tags[tag]["threads"][thread_ident]["current_start"] = None
profiler_tags[tag]["threads"][thread_ident]["captures"].append(end-begin)
if not profiler_ran:
profiler_ran = True
@staticmethod
def get_profiler(tag=None, super_tag=None):
if tag in Profiler.profilers:
return Profiler.profilers[tag]
else:
if not tag in profiler_tags:
profiler_tags[tag] = {"threads": {}, "super": super_tag}
if not thread_ident in profiler_tags[tag]["threads"]:
profiler_tags[tag]["threads"][thread_ident] = {"current_start": None, "captures": []}
profiler = Profiler(tag, super_tag)
Profiler.profilers[tag] = profiler
return profiler
profiler_tags[tag]["threads"][thread_ident]["current_start"] = time.perf_counter()
def __init__(self, tag=None, super_tag=None):
self.paused = False
self.pause_time = 0
self.pause_started = None
self.tag = tag
self.super_tag = super_tag
if self.super_tag in Profiler.profilers:
self.super_profiler = Profiler.profilers[self.super_tag]
self.pause_super = self.super_profiler.pause
self.resume_super = self.super_profiler.resume
else:
def noop(self=None):
pass
self.super_profiler = None
self.pause_super = noop
self.resume_super = noop
except Exception as e:
trace_exception(e)
def __enter__(self):
self.pause_super()
tag = self.tag
super_tag = self.super_tag
thread_ident = threading.get_ident()
if not tag in Profiler.tags:
Profiler.tags[tag] = {"threads": {}, "super": super_tag}
if not thread_ident in Profiler.tags[tag]["threads"]:
Profiler.tags[tag]["threads"][thread_ident] = {"current_start": None, "captures": []}
def profiler_results():
from statistics import mean, median, stdev
results = {}
for tag in profiler_tags:
tag_captures = []
tag_entry = profiler_tags[tag]
Profiler.tags[tag]["threads"][thread_ident]["current_start"] = time.perf_counter()
self.resume_super()
def __exit__(self, exc_type, exc_value, traceback):
self.pause_super()
tag = self.tag
super_tag = self.super_tag
end = time.perf_counter() - self.pause_time
self.pause_time = 0
thread_ident = threading.get_ident()
if tag in Profiler.tags and thread_ident in Profiler.tags[tag]["threads"]:
if Profiler.tags[tag]["threads"][thread_ident]["current_start"] != None:
begin = Profiler.tags[tag]["threads"][thread_ident]["current_start"]
Profiler.tags[tag]["threads"][thread_ident]["current_start"] = None
Profiler.tags[tag]["threads"][thread_ident]["captures"].append(end-begin)
if not Profiler._ran:
Profiler._ran = True
self.resume_super()
def pause(self, pause_started=None):
if not self.paused:
self.paused = True
self.pause_started = pause_started or time.perf_counter()
self.pause_super(self.pause_started)
def resume(self):
if self.paused:
self.pause_time += time.perf_counter() - self.pause_started
self.paused = False
self.resume_super()
@staticmethod
def ran():
return Profiler._ran
@staticmethod
def results():
from statistics import mean, median, stdev
results = {}
for thread_ident in tag_entry["threads"]:
thread_entry = tag_entry["threads"][thread_ident]
thread_captures = thread_entry["captures"]
sample_count = len(thread_captures)
for tag in Profiler.tags:
tag_captures = []
tag_entry = Profiler.tags[tag]
if sample_count > 2:
thread_results = {
"count": sample_count,
"mean": mean(thread_captures),
"median": median(thread_captures),
"stdev": stdev(thread_captures)
for thread_ident in tag_entry["threads"]:
thread_entry = tag_entry["threads"][thread_ident]
thread_captures = thread_entry["captures"]
sample_count = len(thread_captures)
if sample_count > 1:
thread_results = {
"count": sample_count,
"mean": mean(thread_captures),
"median": median(thread_captures),
"stdev": stdev(thread_captures)
}
elif sample_count == 1:
thread_results = {
"count": sample_count,
"mean": mean(thread_captures),
"median": median(thread_captures),
"stdev": None
}
tag_captures.extend(thread_captures)
sample_count = len(tag_captures)
if sample_count > 1:
tag_results = {
"name": tag,
"super": tag_entry["super"],
"count": len(tag_captures),
"mean": mean(tag_captures),
"median": median(tag_captures),
"stdev": stdev(tag_captures)
}
elif sample_count == 1:
tag_results = {
"name": tag,
"super": tag_entry["super"],
"count": len(tag_captures),
"mean": mean(tag_captures),
"median": median(tag_captures),
"stdev": None
}
tag_captures.extend(thread_captures)
sample_count = len(tag_captures)
if sample_count > 2:
tag_results = {
"name": tag,
"super": tag_entry["super"],
"count": len(tag_captures),
"mean": mean(tag_captures),
"median": median(tag_captures),
"stdev": stdev(tag_captures)
}
results[tag] = tag_results
def print_results_recursive(tag, results, level=0):
print_tag_results(tag, level+1)
def print_results_recursive(tag, results, level=0):
print_tag_results(tag, level+1)
for tag_name in results:
sub_tag = results[tag_name]
if sub_tag["super"] == tag["name"]:
print_results_recursive(sub_tag, results, level=level+1)
def print_tag_results(tag, level):
ind = " "*level
name = tag["name"]; count = tag["count"]
mean = tag["mean"]; median = tag["median"]; stdev = tag["stdev"]
print( f"{ind}{name}")
print( f"{ind} Samples : {count}")
if stdev != None:
print(f"{ind} Mean : {prettyshorttime(mean)}")
print(f"{ind} Median : {prettyshorttime(median)}")
print(f"{ind} St.dev. : {prettyshorttime(stdev)}")
print( f"{ind} Total : {prettyshorttime(mean*count)}")
print("")
print("\nProfiler results:\n")
for tag_name in results:
sub_tag = results[tag_name]
if sub_tag["super"] == tag["name"]:
print_results_recursive(sub_tag, results, level=level+1)
tag = results[tag_name]
if tag["super"] == None:
print_results_recursive(tag, results)
def print_tag_results(tag, level):
ind = " "*level
name = tag["name"]; count = tag["count"]
mean = tag["mean"]; tag["median"]; stdev = tag["stdev"]
print(f"{ind}{name}")
print(f"{ind} Samples : {count}")
print(f"{ind} Mean : {prettyshorttime(mean)}")
print(f"{ind} Median : {prettyshorttime(median)}")
print(f"{ind} St.dev. : {prettyshorttime(stdev)}")
print("")
print("\nProfiler results:\n")
for tag_name in results:
tag = results[tag_name]
if tag["super"] == None:
print_results_recursive(tag, results)
profile = Profiler.get_profiler
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.8.6"
__version__ = "0.9.1"
+4 -2
View File
@@ -1,5 +1,7 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
py_modules = glob.glob(os.path.dirname(__file__)+"/*.py")
pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
modules = py_modules+pyc_modules
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -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: a6abb7db1df16737cc5d923272a50d50
config: 86f498988bf4f7be49e45444e901c56a
tags: 645f666f9bcd5a90fca523b33c5a78b7
+207 -50
View File
@@ -27,9 +27,14 @@ and install them offline using ``pip``:
pip install ./rns-0.5.1-py3-none-any.whl
For more detailed installation instructions, please see the
:ref:`Platform-Specific Install Notes<install-guides>` section.
After installation is complete, it might be helpful to refer to the
:ref:`Using Reticulum on Your System<using-main>` chapter.
Resolving Dependency & Installation Issues
=============================================
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
On some platforms, there may not be binary packages available for all dependencies, and
``pip`` installation may fail with an error message. In these cases, the issue can usually
be resolved by installing the development essentials packages for your platform:
@@ -100,10 +105,12 @@ 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.
.. 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. In some cases, you may even need to
manually add the ``pip`` install path to your ``PATH`` environment variable.
Sideband
^^^^^^^^
@@ -305,6 +312,20 @@ you are welcome to head over to the `GitHub discussion pages <https://github.com
and propose adding an interface for the hardware.
Creating and Using Custom Interfaces
===========================================
While Reticulum includes a flexible and broad range of built-in interfaces, these
will not cover every conceivable type of communications hardware that Reticulum
can potentially use to communicate.
It is therefore possible to easily write your own interface modules, that can be
loaded at run-time and used on-par with any of the built-in interface types.
For more information on this subject, and code examples to build on, please see
the :ref:`Configuring Interfaces<interfaces-main>` chapter.
Develop a Program with Reticulum
===========================================
If you want to develop programs that use Reticulum, the easiest way to get
@@ -318,14 +339,8 @@ The above command will install Reticulum and dependencies, and you will be
ready to import and use RNS in your own programs. The next step will most
likely be to look at some :ref:`Example Programs<examples-main>`.
For extended functionality, you can install optional dependencies:
.. code::
pip install pyserial
Further information can be found in the :ref:`API Reference<api-main>`.
The entire Reticulum API is documented in the :ref:`API Reference<api-main>`
chapter of this manual.
Participate in Reticulum Development
@@ -373,6 +388,7 @@ your first pull request, it is probably a good idea to introduce yourself on
the `disucssion forum on GitHub <https://github.com/markqvist/Reticulum/discussions>`_,
or ask one of the developers or maintainers for a good place to start.
.. _install-guides:
Platform-Specific Install Notes
==============================================
@@ -451,7 +467,7 @@ here at a later point. Until then you can use the `Sideband source code <https:/
ARM64
^^^^^^^^^^^^^^^^^^^^^^^^
On some architectures, including ARM64, not all dependencies have precompiled
binaries. On such systems, you may need to install ``python3-dev`` before
binaries. On such systems, you may need to install ``python3-dev`` (or similar) before
installing Reticulum or programs that depend on Reticulum.
.. code::
@@ -463,29 +479,8 @@ installing Reticulum or programs that depend on Reticulum.
# Install Reticulum
python3 -m pip install rns
Raspberry Pi
^^^^^^^^^^^^^^^^^^^^^^^^^
It is currently recommended to use a 64-bit version of the Raspberry Pi OS
if you want to run Reticulum on Raspberry Pi computers, since 32-bit versions
don't always have packages available for some dependencies.
While it is possible to install and run Reticulum on 32-bit Rasperry Pi OSes,
it will require manually configuring and installing some packages, and is not
detailed in this manual.
OpenWRT
^^^^^^^^^^^^^^^^^^^^^^^^^
On OpenWRT systems with sufficient storage and memory, you can easily install
Reticulum and related utilities using the `opkg` package manager and `pip`.
.. code::
# Install dependencies
opkg install python3 python3-pip python3-cryptography python3-pyserial
# Install Reticulum
pip install rns
With these packages installed, ``pip`` will be able to build any missing dependencies
on your system locally.
Debian Bookworm
@@ -523,10 +518,145 @@ option, you can use the following command:
pip install rns --break-system-packages
Please note that the "break-system-packages" directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems.
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
MacOS
^^^^^^^^^^^^^^^^^^^^^^^^^
To install Reticulum on macOS, you will need to have Python and the ``pip`` package
manager installed.
Systems running macOS can vary quite widely in whether or not Python is pre-installed,
and if it is, which version is installed, and whether the ``pip`` package manager is
also installed and set up. If in doubt, you can `download and install <https://www.python.org/downloads/>`_
Python manually.
When Python and ``pip`` is available on your system, simply open a terminal window
and use one of the following commands:
.. code::
# Install Reticulum and utilities with pip:
pip3 install rns
# On some versions, you may need to use the
# flag --break-system-packages to install:
pip3 install rns --break-system-packages
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
Additionally, some version combinations of macOS and Python require you to
manually add your installed ``pip`` packages directory to your `PATH` environment
variable, before you can use installed commands in your terminal. Usually, adding
the following line to your shell init script (for example ``~/.zshrc``) will be enough:
.. code::
export PATH=$PATH:~/Library/Python/3.9/bin
Adjust Python version and shell init script location according to your system.
OpenWRT
^^^^^^^^^^^^^^^^^^^^^^^^^
On OpenWRT systems with sufficient storage and memory, you can install
Reticulum and related utilities using the `opkg` package manager and `pip`.
.. note::
At the time of releasing this manual, work is underway to create pre-built
Reticulum packages for OpenWRT, with full configuration, service
and ``uci`` integration. Please see the `feed-reticulum <https://github.com/gretel/feed-reticulum>`_
and `reticulum-openwrt <https://github.com/gretel/reticulum-openwrt>`_
repositories for more information.
To install Reticulum on OpenWRT, first log into a command line session, and
then use the following instructions:
.. code::
# Install dependencies
opkg install python3 python3-pip python3-cryptography python3-pyserial
# Install Reticulum
pip install rns
# Start rnsd with debug logging enabled
rnsd -vvv
.. note::
The above instructions have been verified and tested on OpenWRT 21.02 only.
It is likely that other versions may require slightly altered installation
commands or package names. You will also need enough free space in your
overlay FS, and enough free RAM to actually run Reticulum and any related
programs and utilities.
Depending on your device configuration, you may need to adjust firewall rules
for Reticulum connectivity to and from your device to work. Until proper
packaging is ready, you will also need to manually create a service or startup
script to automatically laucnh Reticulum at boot time.
Please also note that the `AutoInterface` requires link-local IPv6 addresses
to be enabled for any Ethernet and WiFi devices you intend to use. If ``ip a``
shows an address starting with ``fe80::`` for the device in question,
``AutoInterface`` should work for that device.
Raspberry Pi
^^^^^^^^^^^^^^^^^^^^^^^^^
It is currently recommended to use a 64-bit version of the Raspberry Pi OS
if you want to run Reticulum on Raspberry Pi computers, since 32-bit versions
don't always have packages available for some dependencies. If Python and the
`pip` package manager is not already installed, do that first, and then
install Reticulum using `pip`.
.. code::
# Install dependencies
sudo apt install python3 python3-pip python3-cryptography python3-pyserial
# Install Reticulum
pip install rns --break-system-packages
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
While it is possible to install and run Reticulum on 32-bit Rasperry Pi OSes,
it will require manually configuring and installing required build dependencies,
and is not detailed in this manual.
RISC-V
^^^^^^^^^^^^^^^^^^^^^^^^
On some architectures, including RISC-V, not all dependencies have precompiled
binaries. On such systems, you may need to install ``python3-dev`` (or similar) before
installing Reticulum or programs that depend on Reticulum.
.. code::
# Install Python and development packages
sudo apt update
sudo apt install python3 python3-pip python3-dev
# Install Reticulum
python3 -m pip install rns
With these packages installed, ``pip`` will be able to build any missing dependencies
on your system locally.
Ubuntu Lunar
@@ -564,13 +694,45 @@ option, you can use the following command:
pip install rns --break-system-packages
Please note that the "break-system-packages" directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this _could_ in rare
cases lead to version conflicts, it does not generally pose any problems.
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
Windows
^^^^^^^^^^^^^^^^^^^^^^^^^
On Windows operating systems, the easiest way to install Reticulum is by using the
``pip`` package manager from the command line (either the command prompt or Windows
Powershell).
If you don't already have Python installed, `download and install Python <https://www.python.org/downloads/>`_.
At the time of publication of this manual, the recommended version is `Python 3.12.7 <https://www.python.org/downloads/release/python-3127>`_.
**Important!** When asked by the installer, make sure to add the Python program to
your PATH environment variables. If you don't do this, you will not be able to
use the ``pip`` installer, or run the included Reticulum utility programs (such as
``rnsd`` and ``rnstatus``) from the command line.
After installing Python, open the command prompt or Windows Powershell, and type:
.. code::
pip install rns
You can now use Reticulum and all included utility programs directly from your
preferred command line interface.
Pure-Python Reticulum
==============================================
.. warning::
If you use the ``rnspure`` package to run Reticulum on systems that
do not support `PyCA/cryptography <https://github.com/pyca/cryptography>`_, it is
important that you read and understand the :ref:`Cryptographic Primitives <understanding-primitives>`
section of this manual.
In some rare cases, and on more obscure system types, it is not possible to
install one or more dependencies. In such situations,
you can use the ``rnspure`` package instead of the ``rns`` package, or use ``pip``
@@ -585,8 +747,3 @@ only if they are *needed* and *available*. If for example you want to use Reticu
on a system that cannot support ``pyserial``, it is perfectly possible to do so using
the `rnspure` package, but Reticulum will not be able to use serial-based interfaces.
All other available modules will still be loaded when needed.
**Please Note!** If you use the `rnspure` package to run Reticulum on systems that
do not support `PyCA/cryptography <https://github.com/pyca/cryptography>`_, it is
important that you read and understand the :ref:`Cryptographic Primitives <understanding-primitives>`
section of this manual.
+4 -4
View File
@@ -75,8 +75,8 @@ completely from scratch, to your exact desired specifications, this chapter
will explain the easiest possible approach to creating RNodes: Using common
LoRa development boards. This approach can be boiled down to two simple steps:
1. Obtain one or more supported development boards
2. Install the RNode firmware with the automated installer
1. Obtain one or more :ref:`supported development boards<rnode-supported>`
2. Install the RNode firmware with the :ref:`automated installer<rnode-installation>`
Once the firmware has been installed and provisioned by the install script, it
is ready to use with any software that supports RNodes, including Reticulum.
@@ -85,8 +85,8 @@ to the configuration.
.. _rnode-supported:
Supported Boards
^^^^^^^^^^^^^^^^
Supported Boards and Devices
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To create one or more RNodes, you will need to obtain supported development
boards. The following boards are supported by the auto-installer.
+38 -14
View File
@@ -162,11 +162,12 @@ 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.
.. 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
@@ -238,8 +239,9 @@ can simply specify the Yggdrasil ``tun`` device and a listening port, like so:
device = tun0
listen_port = 4343
**Please Note!** The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:
.. note::
The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:
.. code::
@@ -311,8 +313,9 @@ 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:
.. note::
The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:
.. code::
@@ -334,11 +337,12 @@ private and the internet. It can also allow broadcast communication
over IP networks, so it can provide an easy way to enable connectivity
with all other peers on a local area network.
*Please Note!* Using broadcast UDP traffic has performance implications,
especially on WiFi. If your goal is simply to enable easy communication
with all peers in your local Ethernet broadcast domain, the
:ref:`Auto Interface<interfaces-auto>` performs better, and is even
easier to use.
.. warning::
Using broadcast UDP traffic has performance implications,
especially on WiFi. If your goal is simply to enable easy communication
with all peers in your local Ethernet broadcast domain, the
:ref:`Auto Interface<interfaces-auto>` performs better, and is even
easier to use.
.. code::
@@ -393,6 +397,11 @@ RNode LoRa Interface
To use Reticulum over LoRa, the `RNode <https://unsigned.io/rnode/>`_ interface
can be used, and offers full control over LoRa parameters.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
# Here's an example of how to add a LoRa interface
@@ -480,6 +489,11 @@ RNode Multi Interface
For RNodes that support multiple LoRa transceivers, the RNode
Multi interface can be used to configure sub-interfaces individually.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
# Here's an example of how to add an RNode Multi interface
@@ -647,6 +661,11 @@ radio modems and TNCs, including `OpenModem <https://unsigned.io/openmodem/>`_.
KISS interfaces can also be configured to periodically send out beacons
for station identification purposes.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
[[Packet Radio KISS Interface]]
@@ -710,6 +729,11 @@ encapsulate in AX.25.
A more efficient way is to use the plain KISS interface with the
beaconing functionality described above.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
[[Packet Radio AX.25 KISS Interface]]
+3 -1
View File
@@ -32,7 +32,9 @@ Provide Feedback
================
All feedback on the usage, functioning and potential dysfunctioning of any and
all components of the system is very valuable to the continued development and
improvement of Reticulum. Absolutely no automated analytics, telemetry, error
improvement of Reticulum.
Absolutely no automated analytics, telemetry, error
reporting or statistics is collected and reported by Reticulum under any
circumstances, so we rely on old-fashioned human feedback.
+4 -3
View File
@@ -904,6 +904,7 @@ with the OpenSSL backend being *much* faster. The most important consequence how
potential loss of security by using primitives that has not seen the same amount of scrutiny,
testing and review as those from OpenSSL.
If you want to use the internal pure-python primitives, it is **highly advisable** that you
have a good understanding of the risks that this pose, and make an informed decision on whether
those risks are acceptable to you.
.. warning::
If you want to use the internal pure-python primitives, it is **highly advisable** that you
have a good understanding of the risks that this pose, and make an informed decision on whether
those risks are acceptable to you.
+1 -1
View File
@@ -189,4 +189,4 @@ such. While it has been built with cryptography best-practices very foremost in
mind, it has not yet been externally security audited, and there could very well be
privacy-breaking bugs. To be considered secure, Reticulum needs a thorough
security review by independent cryptographers and security researchers. If you
want to help out with this, or can help sponsor an audit, please do get in touch.
want to help out with this, or can help sponsor an audit, please do get in touch.
+1 -1
View File
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.8.6 beta',
VERSION: '0.9.1 beta',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
+33 -36
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Code Examples - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Code Examples - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -238,6 +238,7 @@ program.</p>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
@@ -331,7 +332,7 @@ program.</p>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Minimal.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Minimal.py</a>.</p>
@@ -349,6 +350,7 @@ notifications about announces from relevant destinations.</p>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
@@ -511,7 +513,7 @@ notifications about announces from relevant destinations.</p>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Announce.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Announce.py</a>.</p>
@@ -640,7 +642,7 @@ over the network.</p>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Broadcast.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Broadcast.py</a>.</p>
@@ -657,6 +659,7 @@ the Packet interface.</p>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
@@ -781,7 +784,7 @@ the Packet interface.</p>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -979,7 +982,7 @@ the Packet interface.</p>
<span class="n">client</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span><span class="p">,</span> <span class="n">configarg</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeoutarg</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Echo.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Echo.py</a>.</p>
@@ -1109,7 +1112,7 @@ destination, and passing traffic back and forth over the link.</p>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -1212,9 +1215,8 @@ destination, and passing traffic back and forth over the link.</p>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When a packet is received over the link, we</span>
<span class="c1"># simply print out the data.</span>
@@ -1278,7 +1280,7 @@ destination, and passing traffic back and forth over the link.</p>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py</a>.</p>
@@ -1422,7 +1424,7 @@ the link has been established.</p>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -1534,9 +1536,8 @@ the link has been established.</p>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When a packet is received over the link, we</span>
<span class="c1"># simply print out the data.</span>
@@ -1600,7 +1601,7 @@ the link has been established.</p>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py</a>.</p>
@@ -1729,7 +1730,7 @@ the link has been established.</p>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -1836,9 +1837,8 @@ the link has been established.</p>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1">##########################################################</span>
@@ -1894,7 +1894,7 @@ the link has been established.</p>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py</a>.</p>
@@ -2029,7 +2029,7 @@ data between peers of a <code class="docutils literal notranslate"><span class="
<span class="k">def</span> <span class="nf">server_loop</span><span class="p">(</span><span class="n">destination</span><span class="p">):</span>
<span class="c1"># Let the user know that everything is ready</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Link example &quot;</span><span class="o">+</span>
<span class="s2">&quot;Channel example &quot;</span><span class="o">+</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">)</span><span class="o">+</span>
<span class="s2">&quot; running, waiting for a connection.&quot;</span>
<span class="p">)</span>
@@ -2117,7 +2117,7 @@ data between peers of a <code class="docutils literal notranslate"><span class="
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -2181,7 +2181,7 @@ data between peers of a <code class="docutils literal notranslate"><span class="
<span class="n">packed_size</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">message</span><span class="o">.</span><span class="n">pack</span><span class="p">())</span>
<span class="n">channel</span> <span class="o">=</span> <span class="n">server_link</span><span class="o">.</span><span class="n">get_channel</span><span class="p">()</span>
<span class="k">if</span> <span class="n">channel</span><span class="o">.</span><span class="n">is_ready_to_send</span><span class="p">():</span>
<span class="k">if</span> <span class="n">packed_size</span> <span class="o">&lt;=</span> <span class="n">channel</span><span class="o">.</span><span class="n">MDU</span><span class="p">:</span>
<span class="k">if</span> <span class="n">packed_size</span> <span class="o">&lt;=</span> <span class="n">channel</span><span class="o">.</span><span class="n">mdu</span><span class="p">:</span>
<span class="n">channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
@@ -2226,9 +2226,8 @@ data between peers of a <code class="docutils literal notranslate"><span class="
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When a packet is received over the channel, we</span>
<span class="c1"># simply print out the data.</span>
@@ -2292,7 +2291,7 @@ data between peers of a <code class="docutils literal notranslate"><span class="
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Channel.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Channel.py</a>.</p>
@@ -2460,7 +2459,7 @@ binary data between peers of a <code class="docutils literal notranslate"><span
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -2557,9 +2556,8 @@ binary data between peers of a <code class="docutils literal notranslate"><span
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When the buffer has new data, read it and write it to the terminal.</span>
<span class="k">def</span> <span class="nf">client_buffer_ready</span><span class="p">(</span><span class="n">ready_bytes</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
@@ -2623,7 +2621,7 @@ binary data between peers of a <code class="docutils literal notranslate"><span
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Buffer.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Buffer.py</a>.</p>
@@ -2859,7 +2857,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
@@ -3097,7 +3095,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="k">global</span> <span class="n">server_files</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">server_files</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Timed out waiting for filelist, exiting&quot;</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When a link is closed, we&#39;ll inform the</span>
@@ -3110,9 +3108,8 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When RNS detects that the download has</span>
<span class="c1"># started, we&#39;ll update our menu state</span>
@@ -3236,7 +3233,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Filetransfer.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Filetransfer.py</a>.</p>
+4 -4
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
+10 -6
View File
@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.8.6 beta documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -139,7 +139,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -165,7 +165,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -508,14 +508,18 @@
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link">Link (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.LINK_MTU_DISCOVERY">LINK_MTU_DISCOVERY (RNS.Reticulum attribute)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.link_mtu_discovery">link_mtu_discovery() (RNS.Reticulum static method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Destination.load_private_key">load_private_key() (RNS.Destination method)</a>
<ul>
<li><a href="reference.html#RNS.Identity.load_private_key">(RNS.Identity method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity.load_public_key">load_public_key() (RNS.Identity method)</a>
</li>
</ul></td>
@@ -526,7 +530,7 @@
<h2>M</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Channel.Channel.MDU">MDU (RNS.Channel.Channel property)</a>
<li><a href="reference.html#RNS.Channel.Channel.mdu">mdu (RNS.Channel.Channel property)</a>
</li>
<li><a href="reference.html#RNS.MessageBase">MessageBase (class in RNS)</a>
</li>
+203 -51
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Getting Started Fast - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Getting Started Fast - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -241,9 +241,12 @@ and install them offline using <code class="docutils literal notranslate"><span
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="o">./</span><span class="n">rns</span><span class="o">-</span><span class="mf">0.5.1</span><span class="o">-</span><span class="n">py3</span><span class="o">-</span><span class="n">none</span><span class="o">-</span><span class="nb">any</span><span class="o">.</span><span class="n">whl</span>
</pre></div>
</div>
</section>
<p>For more detailed installation instructions, please see the
<a class="reference internal" href="#install-guides"><span class="std std-ref">Platform-Specific Install Notes</span></a> section.</p>
<p>After installation is complete, it might be helpful to refer to the
<a class="reference internal" href="using.html#using-main"><span class="std std-ref">Using Reticulum on Your System</span></a> chapter.</p>
<section id="resolving-dependency-installation-issues">
<h2>Resolving Dependency &amp; Installation Issues<a class="headerlink" href="#resolving-dependency-installation-issues" title="Permalink to this heading">#</a></h2>
<h3>Resolving Dependency &amp; Installation Issues<a class="headerlink" href="#resolving-dependency-installation-issues" title="Permalink to this heading">#</a></h3>
<p>On some platforms, there may not be binary packages available for all dependencies, and
<code class="docutils literal notranslate"><span class="pre">pip</span></code> installation may fail with an error message. In these cases, the issue can usually
be resolved by installing the development essentials packages for your platform:</p>
@@ -261,6 +264,7 @@ be resolved by installing the development essentials packages for your platform:
dependencies from source, and complete installation even on platforms that dont have pre-
compiled packages available.</p>
</section>
</section>
<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 heading">#</a></h2>
<p>If you simply want to try using a program built with Reticulum, a few different
@@ -288,7 +292,8 @@ provides a complete encrypted communications suite built with Reticulum. It feat
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>
<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
<a class="reference external" href="https://github.com/markqvist/lxmf">LXMF</a>, another project built with Reticulum.</p>
@@ -300,17 +305,22 @@ 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
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>If this is the very first time you use <code class="docutils literal notranslate"><span class="pre">pip</span></code> 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>
program, reboot your system and try again. In some cases, you may even need to
manually add the <code class="docutils literal notranslate"><span class="pre">pip</span></code> install path to your <code class="docutils literal notranslate"><span class="pre">PATH</span></code> environment variable.</p>
</div>
</section>
<section id="sideband">
<h3>Sideband<a class="headerlink" href="#sideband" title="Permalink to this heading">#</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, macOS and Windows.</p>
<a class="reference external image-reference" href="_images/sideband_devices.webp"><img alt="_images/sideband_devices.webp" class="align-center" src="_images/sideband_devices.webp" /></a>
<a class="reference external image-reference" href="_images/sideband_devices.webp"><img alt="_images/sideband_devices.webp" class="align-center" src="_images/sideband_devices.webp" />
</a>
<p>Sideband allows you to communicate with other people or LXMF-compatible
systems over Reticulum networks using LoRa, Packet Radio, WiFi, I2P, Encrypted QR
Paper Messages, or anything else Reticulum supports. It also interoperates with
@@ -321,7 +331,8 @@ the Nomad Network program.</p>
<p>The <a class="reference external" href="https://github.com/liamcottle/reticulum-meshchat">Reticulum MeshChat</a> application
is a user-friendly LXMF client for macOS and Windows, that also includes voice call
functionality, and a range of other interesting functions.</p>
<a class="reference external image-reference" href="_images/meshchat_1.webp"><img alt="_images/meshchat_1.webp" class="align-center" src="_images/meshchat_1.webp" /></a>
<a class="reference external image-reference" href="_images/meshchat_1.webp"><img alt="_images/meshchat_1.webp" class="align-center" src="_images/meshchat_1.webp" />
</a>
<p>Reticulum MeshChat is of course also compatible with Sideband and Nomad Network, or
any other LXMF client.</p>
</section>
@@ -456,6 +467,16 @@ refer to these additional external resources:</p>
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>
</section>
<section id="creating-and-using-custom-interfaces">
<h2>Creating and Using Custom Interfaces<a class="headerlink" href="#creating-and-using-custom-interfaces" title="Permalink to this heading">#</a></h2>
<p>While Reticulum includes a flexible and broad range of built-in interfaces, these
will not cover every conceivable type of communications hardware that Reticulum
can potentially use to communicate.</p>
<p>It is therefore possible to easily write your own interface modules, that can be
loaded at run-time and used on-par with any of the built-in interface types.</p>
<p>For more information on this subject, and code examples to build on, please see
the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Configuring Interfaces</span></a> chapter.</p>
</section>
<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 heading">#</a></h2>
<p>If you want to develop programs that use Reticulum, the easiest way to get
@@ -466,11 +487,8 @@ started is to install the latest release of Reticulum via pip:</p>
<p>The above command will install Reticulum and dependencies, and you will be
ready to import and use RNS in your own programs. The next step will most
likely be to look at some <a class="reference internal" href="examples.html#examples-main"><span class="std std-ref">Example Programs</span></a>.</p>
<p>For extended functionality, you can install optional dependencies:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">pyserial</span>
</pre></div>
</div>
<p>Further information can be found in the <a class="reference internal" href="reference.html#api-main"><span class="std std-ref">API Reference</span></a>.</p>
<p>The entire Reticulum API is documented in the <a class="reference internal" href="reference.html#api-main"><span class="std std-ref">API Reference</span></a>
chapter of this manual.</p>
</section>
<section id="participate-in-reticulum-development">
<h2>Participate in Reticulum Development<a class="headerlink" href="#participate-in-reticulum-development" title="Permalink to this heading">#</a></h2>
@@ -516,7 +534,7 @@ the <a class="reference external" href="https://github.com/markqvist/Reticulum/d
or ask one of the developers or maintainers for a good place to start.</p>
</section>
<section id="platform-specific-install-notes">
<h2>Platform-Specific Install Notes<a class="headerlink" href="#platform-specific-install-notes" title="Permalink to this heading">#</a></h2>
<span id="install-guides"></span><h2>Platform-Specific Install Notes<a class="headerlink" href="#platform-specific-install-notes" title="Permalink to this heading">#</a></h2>
<p>Some platforms require a slightly different installation procedure, or have
various quirks that are worth being aware of. These are listed here.</p>
<section id="android">
@@ -581,7 +599,7 @@ here at a later point. Until then you can use the <a class="reference external"
<section id="arm64">
<h3>ARM64<a class="headerlink" href="#arm64" title="Permalink to this heading">#</a></h3>
<p>On some architectures, including ARM64, not all dependencies have precompiled
binaries. On such systems, you may need to install <code class="docutils literal notranslate"><span class="pre">python3-dev</span></code> before
binaries. On such systems, you may need to install <code class="docutils literal notranslate"><span class="pre">python3-dev</span></code> (or similar) before
installing Reticulum or programs that depend on Reticulum.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install Python and development packages</span>
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">update</span>
@@ -591,27 +609,8 @@ installing Reticulum or programs that depend on Reticulum.</p>
<span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">rns</span>
</pre></div>
</div>
</section>
<section id="raspberry-pi">
<h3>Raspberry Pi<a class="headerlink" href="#raspberry-pi" title="Permalink to this heading">#</a></h3>
<p>It is currently recommended to use a 64-bit version of the Raspberry Pi OS
if you want to run Reticulum on Raspberry Pi computers, since 32-bit versions
dont always have packages available for some dependencies.</p>
<p>While it is possible to install and run Reticulum on 32-bit Rasperry Pi OSes,
it will require manually configuring and installing some packages, and is not
detailed in this manual.</p>
</section>
<section id="openwrt">
<h3>OpenWRT<a class="headerlink" href="#openwrt" title="Permalink to this heading">#</a></h3>
<p>On OpenWRT systems with sufficient storage and memory, you can easily install
Reticulum and related utilities using the <cite>opkg</cite> package manager and <cite>pip</cite>.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span>
<span class="n">opkg</span> <span class="n">install</span> <span class="n">python3</span> <span class="n">python3</span><span class="o">-</span><span class="n">pip</span> <span class="n">python3</span><span class="o">-</span><span class="n">cryptography</span> <span class="n">python3</span><span class="o">-</span><span class="n">pyserial</span>
<span class="c1"># Install Reticulum</span>
<span class="n">pip</span> <span class="n">install</span> <span class="n">rns</span>
</pre></div>
</div>
<p>With these packages installed, <code class="docutils literal notranslate"><span class="pre">pip</span></code> will be able to build any missing dependencies
on your system locally.</p>
</section>
<section id="debian-bookworm">
<h3>Debian Bookworm<a class="headerlink" href="#debian-bookworm" title="Permalink to this heading">#</a></h3>
@@ -642,10 +641,132 @@ option, you can use the following command:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>pip install rns --break-system-packages
</pre></div>
</div>
<p>Please note that the “break-system-packages” directive is a somewhat misleading choice
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The <code class="docutils literal notranslate"><span class="pre">--break-system-packages</span></code> directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing <code class="docutils literal notranslate"><span class="pre">pip</span></code> packages user- and system-wide. While this <em>could</em> in rare
cases lead to version conflicts, it does not generally pose any problems.</p>
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.</p>
</div>
</section>
<section id="macos">
<h3>MacOS<a class="headerlink" href="#macos" title="Permalink to this heading">#</a></h3>
<p>To install Reticulum on macOS, you will need to have Python and the <code class="docutils literal notranslate"><span class="pre">pip</span></code> package
manager installed.</p>
<p>Systems running macOS can vary quite widely in whether or not Python is pre-installed,
and if it is, which version is installed, and whether the <code class="docutils literal notranslate"><span class="pre">pip</span></code> package manager is
also installed and set up. If in doubt, you can <a class="reference external" href="https://www.python.org/downloads/">download and install</a>
Python manually.</p>
<p>When Python and <code class="docutils literal notranslate"><span class="pre">pip</span></code> is available on your system, simply open a terminal window
and use one of the following commands:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install Reticulum and utilities with pip:</span>
<span class="n">pip3</span> <span class="n">install</span> <span class="n">rns</span>
<span class="c1"># On some versions, you may need to use the</span>
<span class="c1"># flag --break-system-packages to install:</span>
<span class="n">pip3</span> <span class="n">install</span> <span class="n">rns</span> <span class="o">--</span><span class="k">break</span><span class="o">-</span><span class="n">system</span><span class="o">-</span><span class="n">packages</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The <code class="docutils literal notranslate"><span class="pre">--break-system-packages</span></code> directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing <code class="docutils literal notranslate"><span class="pre">pip</span></code> packages user- and system-wide. While this <em>could</em> in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.</p>
</div>
<p>Additionally, some version combinations of macOS and Python require you to
manually add your installed <code class="docutils literal notranslate"><span class="pre">pip</span></code> packages directory to your <cite>PATH</cite> environment
variable, before you can use installed commands in your terminal. Usually, adding
the following line to your shell init script (for example <code class="docutils literal notranslate"><span class="pre">~/.zshrc</span></code>) will be enough:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>export PATH=$PATH:~/Library/Python/3.9/bin
</pre></div>
</div>
<p>Adjust Python version and shell init script location according to your system.</p>
</section>
<section id="openwrt">
<h3>OpenWRT<a class="headerlink" href="#openwrt" title="Permalink to this heading">#</a></h3>
<p>On OpenWRT systems with sufficient storage and memory, you can install
Reticulum and related utilities using the <cite>opkg</cite> package manager and <cite>pip</cite>.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>At the time of releasing this manual, work is underway to create pre-built
Reticulum packages for OpenWRT, with full configuration, service
and <code class="docutils literal notranslate"><span class="pre">uci</span></code> integration. Please see the <a class="reference external" href="https://github.com/gretel/feed-reticulum">feed-reticulum</a>
and <a class="reference external" href="https://github.com/gretel/reticulum-openwrt">reticulum-openwrt</a>
repositories for more information.</p>
</div>
<p>To install Reticulum on OpenWRT, first log into a command line session, and
then use the following instructions:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span>
<span class="n">opkg</span> <span class="n">install</span> <span class="n">python3</span> <span class="n">python3</span><span class="o">-</span><span class="n">pip</span> <span class="n">python3</span><span class="o">-</span><span class="n">cryptography</span> <span class="n">python3</span><span class="o">-</span><span class="n">pyserial</span>
<span class="c1"># Install Reticulum</span>
<span class="n">pip</span> <span class="n">install</span> <span class="n">rns</span>
<span class="c1"># Start rnsd with debug logging enabled</span>
<span class="n">rnsd</span> <span class="o">-</span><span class="n">vvv</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The above instructions have been verified and tested on OpenWRT 21.02 only.
It is likely that other versions may require slightly altered installation
commands or package names. You will also need enough free space in your
overlay FS, and enough free RAM to actually run Reticulum and any related
programs and utilities.</p>
</div>
<p>Depending on your device configuration, you may need to adjust firewall rules
for Reticulum connectivity to and from your device to work. Until proper
packaging is ready, you will also need to manually create a service or startup
script to automatically laucnh Reticulum at boot time.</p>
<p>Please also note that the <cite>AutoInterface</cite> requires link-local IPv6 addresses
to be enabled for any Ethernet and WiFi devices you intend to use. If <code class="docutils literal notranslate"><span class="pre">ip</span> <span class="pre">a</span></code>
shows an address starting with <code class="docutils literal notranslate"><span class="pre">fe80::</span></code> for the device in question,
<code class="docutils literal notranslate"><span class="pre">AutoInterface</span></code> should work for that device.</p>
</section>
<section id="raspberry-pi">
<h3>Raspberry Pi<a class="headerlink" href="#raspberry-pi" title="Permalink to this heading">#</a></h3>
<p>It is currently recommended to use a 64-bit version of the Raspberry Pi OS
if you want to run Reticulum on Raspberry Pi computers, since 32-bit versions
dont always have packages available for some dependencies. If Python and the
<cite>pip</cite> package manager is not already installed, do that first, and then
install Reticulum using <cite>pip</cite>.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span>
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">python3</span> <span class="n">python3</span><span class="o">-</span><span class="n">pip</span> <span class="n">python3</span><span class="o">-</span><span class="n">cryptography</span> <span class="n">python3</span><span class="o">-</span><span class="n">pyserial</span>
<span class="c1"># Install Reticulum</span>
<span class="n">pip</span> <span class="n">install</span> <span class="n">rns</span> <span class="o">--</span><span class="k">break</span><span class="o">-</span><span class="n">system</span><span class="o">-</span><span class="n">packages</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The <code class="docutils literal notranslate"><span class="pre">--break-system-packages</span></code> directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing <code class="docutils literal notranslate"><span class="pre">pip</span></code> packages user- and system-wide. While this <em>could</em> in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.</p>
</div>
<p>While it is possible to install and run Reticulum on 32-bit Rasperry Pi OSes,
it will require manually configuring and installing required build dependencies,
and is not detailed in this manual.</p>
</section>
<section id="risc-v">
<h3>RISC-V<a class="headerlink" href="#risc-v" title="Permalink to this heading">#</a></h3>
<p>On some architectures, including RISC-V, not all dependencies have precompiled
binaries. On such systems, you may need to install <code class="docutils literal notranslate"><span class="pre">python3-dev</span></code> (or similar) before
installing Reticulum or programs that depend on Reticulum.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install Python and development packages</span>
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">update</span>
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">python3</span> <span class="n">python3</span><span class="o">-</span><span class="n">pip</span> <span class="n">python3</span><span class="o">-</span><span class="n">dev</span>
<span class="c1"># Install Reticulum</span>
<span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">rns</span>
</pre></div>
</div>
<p>With these packages installed, <code class="docutils literal notranslate"><span class="pre">pip</span></code> will be able to build any missing dependencies
on your system locally.</p>
</section>
<section id="ubuntu-lunar">
<h3>Ubuntu Lunar<a class="headerlink" href="#ubuntu-lunar" title="Permalink to this heading">#</a></h3>
@@ -676,14 +797,43 @@ option, you can use the following command:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>pip install rns --break-system-packages
</pre></div>
</div>
<p>Please note that the “break-system-packages” directive is a somewhat misleading choice
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The <code class="docutils literal notranslate"><span class="pre">--break-system-packages</span></code> directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing <code class="docutils literal notranslate"><span class="pre">pip</span></code> packages user- and system-wide. While this _could_ in rare
cases lead to version conflicts, it does not generally pose any problems.</p>
allow installing <code class="docutils literal notranslate"><span class="pre">pip</span></code> packages user- and system-wide. While this <em>could</em> in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.</p>
</div>
</section>
<section id="windows">
<h3>Windows<a class="headerlink" href="#windows" title="Permalink to this heading">#</a></h3>
<p>On Windows operating systems, the easiest way to install Reticulum is by using the
<code class="docutils literal notranslate"><span class="pre">pip</span></code> package manager from the command line (either the command prompt or Windows
Powershell).</p>
<p>If you dont already have Python installed, <a class="reference external" href="https://www.python.org/downloads/">download and install Python</a>.
At the time of publication of this manual, the recommended version is <a class="reference external" href="https://www.python.org/downloads/release/python-3127">Python 3.12.7</a>.</p>
<p><strong>Important!</strong> When asked by the installer, make sure to add the Python program to
your PATH environment variables. If you dont do this, you will not be able to
use the <code class="docutils literal notranslate"><span class="pre">pip</span></code> installer, or run the included Reticulum utility programs (such as
<code class="docutils literal notranslate"><span class="pre">rnsd</span></code> and <code class="docutils literal notranslate"><span class="pre">rnstatus</span></code>) from the command line.</p>
<p>After installing Python, open the command prompt or Windows Powershell, and type:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">rns</span>
</pre></div>
</div>
<p>You can now use Reticulum and all included utility programs directly from your
preferred command line interface.</p>
</section>
</section>
<section id="pure-python-reticulum">
<h2>Pure-Python Reticulum<a class="headerlink" href="#pure-python-reticulum" title="Permalink to this heading">#</a></h2>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>If you use the <code class="docutils literal notranslate"><span class="pre">rnspure</span></code> package to run Reticulum on systems that
do not support <a class="reference external" href="https://github.com/pyca/cryptography">PyCA/cryptography</a>, it is
important that you read and understand the <a class="reference internal" href="understanding.html#understanding-primitives"><span class="std std-ref">Cryptographic Primitives</span></a>
section of this manual.</p>
</div>
<p>In some rare cases, and on more obscure system types, it is not possible to
install one or more dependencies. In such situations,
you can use the <code class="docutils literal notranslate"><span class="pre">rnspure</span></code> package instead of the <code class="docutils literal notranslate"><span class="pre">rns</span></code> package, or use <code class="docutils literal notranslate"><span class="pre">pip</span></code>
@@ -697,10 +847,6 @@ only if they are <em>needed</em> and <em>available</em>. If for example you want
on a system that cannot support <code class="docutils literal notranslate"><span class="pre">pyserial</span></code>, it is perfectly possible to do so using
the <cite>rnspure</cite> package, but Reticulum will not be able to use serial-based interfaces.
All other available modules will still be loaded when needed.</p>
<p><strong>Please Note!</strong> If you use the <cite>rnspure</cite> package to run Reticulum on systems that
do not support <a class="reference external" href="https://github.com/pyca/cryptography">PyCA/cryptography</a>, it is
important that you read and understand the <a class="reference internal" href="understanding.html#understanding-primitives"><span class="std std-ref">Cryptographic Primitives</span></a>
section of this manual.</p>
</section>
</section>
@@ -761,8 +907,10 @@ section of this manual.</p>
<div class="toc-tree">
<ul>
<li><a class="reference internal" href="#">Getting Started Fast</a><ul>
<li><a class="reference internal" href="#standalone-reticulum-installation">Standalone Reticulum Installation</a></li>
<li><a class="reference internal" href="#standalone-reticulum-installation">Standalone Reticulum Installation</a><ul>
<li><a class="reference internal" href="#resolving-dependency-installation-issues">Resolving Dependency &amp; Installation Issues</a></li>
</ul>
</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="#remote-shell">Remote Shell</a></li>
<li><a class="reference internal" href="#nomad-network">Nomad Network</a></li>
@@ -775,15 +923,19 @@ section of this manual.</p>
<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="#adding-radio-interfaces">Adding Radio Interfaces</a></li>
<li><a class="reference internal" href="#creating-and-using-custom-interfaces">Creating and Using Custom Interfaces</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="#platform-specific-install-notes">Platform-Specific Install Notes</a><ul>
<li><a class="reference internal" href="#android">Android</a></li>
<li><a class="reference internal" href="#arm64">ARM64</a></li>
<li><a class="reference internal" href="#raspberry-pi">Raspberry Pi</a></li>
<li><a class="reference internal" href="#openwrt">OpenWRT</a></li>
<li><a class="reference internal" href="#debian-bookworm">Debian Bookworm</a></li>
<li><a class="reference internal" href="#macos">MacOS</a></li>
<li><a class="reference internal" href="#openwrt">OpenWRT</a></li>
<li><a class="reference internal" href="#raspberry-pi">Raspberry Pi</a></li>
<li><a class="reference internal" href="#risc-v">RISC-V</a></li>
<li><a class="reference internal" href="#ubuntu-lunar">Ubuntu Lunar</a></li>
<li><a class="reference internal" href="#windows">Windows</a></li>
</ul>
</li>
<li><a class="reference internal" href="#pure-python-reticulum">Pure-Python Reticulum</a></li>
+37 -23
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Communications Hardware - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Communications Hardware - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -279,20 +279,21 @@ completely from scratch, to your exact desired specifications, this chapter
will explain the easiest possible approach to creating RNodes: Using common
LoRa development boards. This approach can be boiled down to two simple steps:</p>
<ol class="arabic simple">
<li><p>Obtain one or more supported development boards</p></li>
<li><p>Install the RNode firmware with the automated installer</p></li>
<li><p>Obtain one or more <a class="reference internal" href="#rnode-supported"><span class="std std-ref">supported development boards</span></a></p></li>
<li><p>Install the RNode firmware with the <a class="reference internal" href="#rnode-installation"><span class="std std-ref">automated installer</span></a></p></li>
</ol>
<p>Once the firmware has been installed and provisioned by the install script, it
is ready to use with any software that supports RNodes, including Reticulum.
The device can be used with Reticulum by adding an <a class="reference internal" href="interfaces.html#interfaces-rnode"><span class="std std-ref">RNodeInterface</span></a>
to the configuration.</p>
</section>
<section id="supported-boards">
<span id="rnode-supported"></span><h3>Supported Boards<a class="headerlink" href="#supported-boards" title="Permalink to this heading">#</a></h3>
<section id="supported-boards-and-devices">
<span id="rnode-supported"></span><h3>Supported Boards and Devices<a class="headerlink" href="#supported-boards-and-devices" title="Permalink to this heading">#</a></h3>
<p>To create one or more RNodes, you will need to obtain supported development
boards. The following boards are supported by the auto-installer.</p>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_tbeam_supreme.png"><img alt="_images/board_tbeam_supreme.png" class="align-center" src="_images/board_tbeam_supreme.png" style="width: 75%;" /></a>
<a class="reference internal image-reference" href="_images/board_tbeam_supreme.png"><img alt="_images/board_tbeam_supreme.png" class="align-center" src="_images/board_tbeam_supreme.png" style="width: 75%;" />
</a>
<section id="lilygo-t-beam-supreme">
<h4>LilyGO T-Beam Supreme<a class="headerlink" href="#lilygo-t-beam-supreme" title="Permalink to this heading">#</a></h4>
<ul class="simple">
@@ -301,7 +302,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_tbeam.png"><img alt="_images/board_tbeam.png" class="align-center" src="_images/board_tbeam.png" style="width: 75%;" /></a>
<a class="reference internal image-reference" href="_images/board_tbeam.png"><img alt="_images/board_tbeam.png" class="align-center" src="_images/board_tbeam.png" style="width: 75%;" />
</a>
</section>
<section id="lilygo-t-beam">
<h4>LilyGO T-Beam<a class="headerlink" href="#lilygo-t-beam" title="Permalink to this heading">#</a></h4>
@@ -311,7 +313,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_t3s3.png"><img alt="_images/board_t3s3.png" class="align-center" src="_images/board_t3s3.png" style="width: 50%;" /></a>
<a class="reference internal image-reference" href="_images/board_t3s3.png"><img alt="_images/board_t3s3.png" class="align-center" src="_images/board_t3s3.png" style="width: 50%;" />
</a>
</section>
<section id="lilygo-t3s3">
<h4>LilyGO T3S3<a class="headerlink" href="#lilygo-t3s3" title="Permalink to this heading">#</a></h4>
@@ -321,7 +324,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_rak4631.png"><img alt="_images/board_rak4631.png" class="align-center" src="_images/board_rak4631.png" style="width: 45%;" /></a>
<a class="reference internal image-reference" href="_images/board_rak4631.png"><img alt="_images/board_rak4631.png" class="align-center" src="_images/board_rak4631.png" style="width: 45%;" />
</a>
</section>
<section id="rak4631-based-boards">
<h4>RAK4631-based Boards<a class="headerlink" href="#rak4631-based-boards" title="Permalink to this heading">#</a></h4>
@@ -331,7 +335,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://www.rakwireless.com">RAK Wireless</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_rnodev2.png"><img alt="_images/board_rnodev2.png" class="align-center" src="_images/board_rnodev2.png" style="width: 68%;" /></a>
<a class="reference internal image-reference" href="_images/board_rnodev2.png"><img alt="_images/board_rnodev2.png" class="align-center" src="_images/board_rnodev2.png" style="width: 68%;" />
</a>
</section>
<section id="unsigned-rnode-v2-x">
<h4>Unsigned RNode v2.x<a class="headerlink" href="#unsigned-rnode-v2-x" title="Permalink to this heading">#</a></h4>
@@ -341,7 +346,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://unsigned.io">unsigned.io</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_t3v21.png"><img alt="_images/board_t3v21.png" class="align-center" src="_images/board_t3v21.png" style="width: 46%;" /></a>
<a class="reference internal image-reference" href="_images/board_t3v21.png"><img alt="_images/board_t3v21.png" class="align-center" src="_images/board_t3v21.png" style="width: 46%;" />
</a>
</section>
<section id="lilygo-lora32-v2-1">
<h4>LilyGO LoRa32 v2.1<a class="headerlink" href="#lilygo-lora32-v2-1" title="Permalink to this heading">#</a></h4>
@@ -351,7 +357,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_t3v20.png"><img alt="_images/board_t3v20.png" class="align-center" src="_images/board_t3v20.png" style="width: 46%;" /></a>
<a class="reference internal image-reference" href="_images/board_t3v20.png"><img alt="_images/board_t3v20.png" class="align-center" src="_images/board_t3v20.png" style="width: 46%;" />
</a>
</section>
<section id="lilygo-lora32-v2-0">
<h4>LilyGO LoRa32 v2.0<a class="headerlink" href="#lilygo-lora32-v2-0" title="Permalink to this heading">#</a></h4>
@@ -361,7 +368,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_t3v10.png"><img alt="_images/board_t3v10.png" class="align-center" src="_images/board_t3v10.png" style="width: 46%;" /></a>
<a class="reference internal image-reference" href="_images/board_t3v10.png"><img alt="_images/board_t3v10.png" class="align-center" src="_images/board_t3v10.png" style="width: 46%;" />
</a>
</section>
<section id="lilygo-lora32-v1-0">
<h4>LilyGO LoRa32 v1.0<a class="headerlink" href="#lilygo-lora32-v1-0" title="Permalink to this heading">#</a></h4>
@@ -371,7 +379,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_tdeck.png"><img alt="_images/board_tdeck.png" class="align-center" src="_images/board_tdeck.png" style="width: 45%;" /></a>
<a class="reference internal image-reference" href="_images/board_tdeck.png"><img alt="_images/board_tdeck.png" class="align-center" src="_images/board_tdeck.png" style="width: 45%;" />
</a>
</section>
<section id="lilygo-t-deck">
<h4>LilyGO T-Deck<a class="headerlink" href="#lilygo-t-deck" title="Permalink to this heading">#</a></h4>
@@ -381,7 +390,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://lilygo.cn">LilyGO</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_heltec32v30.png"><img alt="_images/board_heltec32v30.png" class="align-center" src="_images/board_heltec32v30.png" style="width: 58%;" /></a>
<a class="reference internal image-reference" href="_images/board_heltec32v30.png"><img alt="_images/board_heltec32v30.png" class="align-center" src="_images/board_heltec32v30.png" style="width: 58%;" />
</a>
</section>
<section id="heltec-lora32-v3-0">
<h4>Heltec LoRa32 v3.0<a class="headerlink" href="#heltec-lora32-v3-0" title="Permalink to this heading">#</a></h4>
@@ -391,7 +401,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://heltec.org">Heltec Automation</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_heltec32v20.png"><img alt="_images/board_heltec32v20.png" class="align-center" src="_images/board_heltec32v20.png" style="width: 58%;" /></a>
<a class="reference internal image-reference" href="_images/board_heltec32v20.png"><img alt="_images/board_heltec32v20.png" class="align-center" src="_images/board_heltec32v20.png" style="width: 58%;" />
</a>
</section>
<section id="heltec-lora32-v2-0">
<h4>Heltec LoRa32 v2.0<a class="headerlink" href="#heltec-lora32-v2-0" title="Permalink to this heading">#</a></h4>
@@ -401,7 +412,8 @@ boards. The following boards are supported by the auto-installer.</p>
<li><p><strong>Manufacturer</strong> <a class="reference external" href="https://heltec.org">Heltec Automation</a></p></li>
</ul>
<hr class="docutils" />
<a class="reference internal image-reference" href="_images/board_rnode.png"><img alt="_images/board_rnode.png" class="align-center" src="_images/board_rnode.png" style="width: 50%;" /></a>
<a class="reference internal image-reference" href="_images/board_rnode.png"><img alt="_images/board_rnode.png" class="align-center" src="_images/board_rnode.png" style="width: 50%;" />
</a>
</section>
<section id="unsigned-rnode-v1-x">
<h4>Unsigned RNode v1.x<a class="headerlink" href="#unsigned-rnode-v1-x" title="Permalink to this heading">#</a></h4>
@@ -450,8 +462,10 @@ Most devices will behave like this by default, or allow it via configuration opt
and start communicating over them using Reticulum. It is not necessary to enable any IP
infrastructure such as DHCP servers, DNS or similar, as long as at least Ethernet is
available, and packets are passed transparently over the physical WiFi-based devices.</p>
<a class="reference internal image-reference" href="_images/radio_rblhg5.png"><img alt="_images/radio_rblhg5.png" src="_images/radio_rblhg5.png" style="width: 49%;" /></a>
<a class="reference internal image-reference" href="_images/radio_is5ac.png"><img alt="_images/radio_is5ac.png" src="_images/radio_is5ac.png" style="width: 49%;" /></a>
<a class="reference internal image-reference" href="_images/radio_rblhg5.png"><img alt="_images/radio_rblhg5.png" src="_images/radio_rblhg5.png" style="width: 49%;" />
</a>
<a class="reference internal image-reference" href="_images/radio_is5ac.png"><img alt="_images/radio_is5ac.png" src="_images/radio_is5ac.png" style="width: 49%;" />
</a>
<p>Below is a list of example WiFi (and similar) radios that work well for high capacity
Reticulum links over long distances:</p>
<ul class="simple">
@@ -550,7 +564,7 @@ can be used with Reticulum. This includes virtual software modems such as
<li><a class="reference internal" href="#combining-hardware-types">Combining Hardware Types</a></li>
<li><a class="reference internal" href="#rnode">RNode</a><ul>
<li><a class="reference internal" href="#creating-rnodes">Creating RNodes</a></li>
<li><a class="reference internal" href="#supported-boards">Supported Boards</a><ul>
<li><a class="reference internal" href="#supported-boards-and-devices">Supported Boards and Devices</a><ul>
<li><a class="reference internal" href="#lilygo-t-beam-supreme">LilyGO T-Beam Supreme</a></li>
<li><a class="reference internal" href="#lilygo-t-beam">LilyGO T-Beam</a></li>
<li><a class="reference internal" href="#lilygo-t3s3">LilyGO T3S3</a></li>
+15 -9
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="#"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -241,8 +241,10 @@ 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#standalone-reticulum-installation">Standalone Reticulum Installation</a></li>
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#resolving-dependency-installation-issues">Resolving Dependency &amp; Installation Issues</a></li>
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#standalone-reticulum-installation">Standalone Reticulum Installation</a><ul>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#resolving-dependency-installation-issues">Resolving Dependency &amp; Installation Issues</a></li>
</ul>
</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#remote-shell">Remote Shell</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#nomad-network">Nomad Network</a></li>
@@ -255,15 +257,19 @@ to participate in the development of Reticulum itself.</p>
<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#adding-radio-interfaces">Adding Radio Interfaces</a></li>
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#creating-and-using-custom-interfaces">Creating and Using Custom Interfaces</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#platform-specific-install-notes">Platform-Specific Install Notes</a><ul>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#android">Android</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#arm64">ARM64</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#raspberry-pi">Raspberry Pi</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#openwrt">OpenWRT</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#debian-bookworm">Debian Bookworm</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#macos">MacOS</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#openwrt">OpenWRT</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#raspberry-pi">Raspberry Pi</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#risc-v">RISC-V</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#ubuntu-lunar">Ubuntu Lunar</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#windows">Windows</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#pure-python-reticulum">Pure-Python Reticulum</a></li>
@@ -322,7 +328,7 @@ to participate in the development of Reticulum itself.</p>
<li class="toctree-l2"><a class="reference internal" href="hardware.html#combining-hardware-types">Combining Hardware Types</a></li>
<li class="toctree-l2"><a class="reference internal" href="hardware.html#rnode">RNode</a><ul>
<li class="toctree-l3"><a class="reference internal" href="hardware.html#creating-rnodes">Creating RNodes</a></li>
<li class="toctree-l3"><a class="reference internal" href="hardware.html#supported-boards">Supported Boards</a></li>
<li class="toctree-l3"><a class="reference internal" href="hardware.html#supported-boards-and-devices">Supported Boards and Devices</a></li>
<li class="toctree-l3"><a class="reference internal" href="hardware.html#installation">Installation</a></li>
<li class="toctree-l3"><a class="reference internal" href="hardware.html#usage-with-reticulum">Usage with Reticulum</a></li>
</ul>
+44 -8
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Configuring Interfaces - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Configuring Interfaces - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -353,11 +353,14 @@ list of I2P base32 addresses to the <code class="docutils literal notranslate"><
<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
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>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>
</div>
<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
@@ -418,8 +421,11 @@ can simply specify the Yggdrasil <code class="docutils literal notranslate"><spa
<span class="n">listen_port</span> <span class="o">=</span> <span class="mi">4343</span>
</pre></div>
</div>
<p><strong>Please Note!</strong> The TCP interfaces support tunneling over I2P, but to do so reliably,
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:</p>
</div>
<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>
@@ -478,8 +484,11 @@ and programs like soundmodems and similar over TCP. When using the
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,
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:</p>
</div>
<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>
@@ -495,11 +504,14 @@ you must use the i2p_tunneled option:</p>
private and the internet. It can also allow broadcast communication
over IP networks, so it can provide an easy way to enable connectivity
with all other peers on a local area network.</p>
<p><em>Please Note!</em> Using broadcast UDP traffic has performance implications,
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Using broadcast UDP traffic has performance implications,
especially on WiFi. If your goal is simply to enable easy communication
with all peers in your local Ethernet broadcast domain, the
<a class="reference internal" href="#interfaces-auto"><span class="std std-ref">Auto Interface</span></a> performs better, and is even
easier to use.</p>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example enables communication with other</span>
<span class="c1"># local Reticulum peers over UDP.</span>
@@ -548,6 +560,12 @@ easier to use.</p>
<span id="interfaces-rnode"></span><h2>RNode LoRa Interface<a class="headerlink" href="#rnode-lora-interface" title="Permalink to this heading">#</a></h2>
<p>To use Reticulum over LoRa, the <a class="reference external" href="https://unsigned.io/rnode/">RNode</a> interface
can be used, and offers full control over LoRa parameters.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.</p>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here&#39;s an example of how to add a LoRa interface</span>
<span class="c1"># using the RNode LoRa transceiver.</span>
@@ -630,6 +648,12 @@ can be used, and offers full control over LoRa parameters.</p>
<span id="interfaces-rnode-multi"></span><h2>RNode Multi Interface<a class="headerlink" href="#rnode-multi-interface" title="Permalink to this heading">#</a></h2>
<p>For RNodes that support multiple LoRa transceivers, the RNode
Multi interface can be used to configure sub-interfaces individually.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.</p>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here&#39;s an example of how to add an RNode Multi interface</span>
<span class="c1"># using the RNode LoRa transceiver.</span>
@@ -784,6 +808,12 @@ Reticulum will try to respawn the program after waiting for <code class="docutil
radio modems and TNCs, including <a class="reference external" href="https://unsigned.io/openmodem/">OpenModem</a>.
KISS interfaces can also be configured to periodically send out beacons
for station identification purposes.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.</p>
</div>
<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>
@@ -841,6 +871,12 @@ layer for anything, and it incurs extra overhead on every packet to
encapsulate in AX.25.</p>
<p>A more efficient way is to use the plain KISS interface with the
beaconing functionality described above.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.</p>
</div>
<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">AX</span><span class="mf">.25</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">AX25KISSInterface</span>
+4 -4
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Building Networks - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Building Networks - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
Binary file not shown.
+38 -9
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>API Reference - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>API Reference - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -261,6 +261,17 @@ 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.LINK_MTU_DISCOVERY">
<span class="sig-name descname"><span class="pre">LINK_MTU_DISCOVERY</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">False</span></em><a class="headerlink" href="#RNS.Reticulum.LINK_MTU_DISCOVERY" title="Permalink to this definition">#</a></dt>
<dd><p>Whether automatic link MTU discovery is enabled by default in this
release. Link MTU discovery significantly increases throughput over
fast links, but requires all intermediary hops to also support it.
Support for this feature was added in RNS version 0.9.0. This option
will become enabled by default in the near future. Please update your
RNS instances.</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="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">2</span></em><a class="headerlink" href="#RNS.Reticulum.ANNOUNCE_CAP" title="Permalink to this definition">#</a></dt>
@@ -319,6 +330,21 @@ and pass announces over the network.</p>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Reticulum.link_mtu_discovery">
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">link_mtu_discovery</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Reticulum.link_mtu_discovery" title="Permalink to this definition">#</a></dt>
<dd><p>Returns whether link MTU discovery is enabled for the running
instance.</p>
<p>When link MTU discovery is enabled, Reticulum will
automatically upgrade link MTUs to the highest supported
value, increasing transfer speed and efficiency.</p>
<dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>True if link MTU discovery is enabled, False if not.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Reticulum.remote_management_enabled">
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">remote_management_enabled</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Reticulum.remote_management_enabled" title="Permalink to this definition">#</a></dt>
@@ -1700,8 +1726,8 @@ returning handler will not be called.</p>
</dd></dl>
<dl class="py property">
<dt class="sig sig-object py" id="RNS.Channel.Channel.MDU">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">MDU</span></span><a class="headerlink" href="#RNS.Channel.Channel.MDU" title="Permalink to this definition">#</a></dt>
<dt class="sig sig-object py" id="RNS.Channel.Channel.mdu">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">mdu</span></span><a class="headerlink" href="#RNS.Channel.Channel.mdu" title="Permalink to this definition">#</a></dt>
<dd><p>Maximum Data Unit: the number of bytes available
for a message to consume in a single send. This
value is adjusted from the <code class="docutils literal notranslate"><span class="pre">Link</span></code> MDU to accommodate
@@ -1943,8 +1969,9 @@ Transport system of Reticulum.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>handler</strong> Must be an object with an <em>aspect_filter</em> attribute and a <em>received_announce(destination_hash, announced_identity, app_data)</em>
callable. Can optionally have a <em>receive_path_responses</em> attribute set to <code class="docutils literal notranslate"><span class="pre">True</span></code>, to also receive all path responses, in addition to live
announces. See the <a class="reference internal" href="examples.html#example-announce"><span class="std std-ref">Announce Example</span></a> for more info.</p>
or <em>received_announce(destination_hash, announced_identity, app_data, announce_packet_hash)</em> callable. Can optionally have a
<em>receive_path_responses</em> attribute set to <code class="docutils literal notranslate"><span class="pre">True</span></code>, to also receive all path responses, in addition to live announces. See
the <a class="reference internal" href="examples.html#example-announce"><span class="std std-ref">Announce Example</span></a> for more info.</p>
</dd>
</dl>
</dd></dl>
@@ -2083,11 +2110,13 @@ will announce it.</p>
<li><a class="reference internal" href="#">API Reference</a><ul>
<li><a class="reference internal" href="#RNS.Reticulum"><code class="docutils literal notranslate"><span class="pre">Reticulum</span></code></a><ul>
<li><a class="reference internal" href="#RNS.Reticulum.MTU"><code class="docutils literal notranslate"><span class="pre">MTU</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.LINK_MTU_DISCOVERY"><code class="docutils literal notranslate"><span class="pre">LINK_MTU_DISCOVERY</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.ANNOUNCE_CAP"><code class="docutils literal notranslate"><span class="pre">ANNOUNCE_CAP</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.MINIMUM_BITRATE"><code class="docutils literal notranslate"><span class="pre">MINIMUM_BITRATE</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.get_instance"><code class="docutils literal notranslate"><span class="pre">get_instance()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.should_use_implicit_proof"><code class="docutils literal notranslate"><span class="pre">should_use_implicit_proof()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.transport_enabled"><code class="docutils literal notranslate"><span class="pre">transport_enabled()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.link_mtu_discovery"><code class="docutils literal notranslate"><span class="pre">link_mtu_discovery()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.remote_management_enabled"><code class="docutils literal notranslate"><span class="pre">remote_management_enabled()</span></code></a></li>
</ul>
</li>
@@ -2221,7 +2250,7 @@ will announce it.</p>
<li><a class="reference internal" href="#RNS.Channel.Channel.remove_message_handler"><code class="docutils literal notranslate"><span class="pre">remove_message_handler()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Channel.Channel.is_ready_to_send"><code class="docutils literal notranslate"><span class="pre">is_ready_to_send()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Channel.Channel.send"><code class="docutils literal notranslate"><span class="pre">send()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Channel.Channel.MDU"><code class="docutils literal notranslate"><span class="pre">MDU</span></code></a></li>
<li><a class="reference internal" href="#RNS.Channel.Channel.mdu"><code class="docutils literal notranslate"><span class="pre">mdu</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#RNS.MessageBase"><code class="docutils literal notranslate"><span class="pre">MessageBase</span></code></a><ul>
+3 -3
View File
@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.8.6 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.9.1 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -138,7 +138,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -164,7 +164,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
File diff suppressed because one or more lines are too long
+6 -5
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Support Reticulum - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Support Reticulum - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -248,7 +248,8 @@ organisation? Make them a reality quickly by sponsoring their implementation.</p
<h2>Provide Feedback<a class="headerlink" href="#provide-feedback" title="Permalink to this heading">#</a></h2>
<p>All feedback on the usage, functioning and potential dysfunctioning of any and
all components of the system is very valuable to the continued development and
improvement of Reticulum. Absolutely no automated analytics, telemetry, error
improvement of Reticulum.</p>
<p>Absolutely no automated analytics, telemetry, error
reporting or statistics is collected and reported by Reticulum under any
circumstances, so we rely on old-fashioned human feedback.</p>
</section>
+7 -4
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Understanding Reticulum - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Understanding Reticulum - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -1099,9 +1099,12 @@ instead use the internal pure-python primitives. A trivial consequence of this i
with the OpenSSL backend being <em>much</em> faster. The most important consequence however, is the
potential loss of security by using primitives that has not seen the same amount of scrutiny,
testing and review as those from OpenSSL.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>If you want to use the internal pure-python primitives, it is <strong>highly advisable</strong> that you
have a good understanding of the risks that this pose, and make an informed decision on whether
those risks are acceptable to you.</p>
</div>
</section>
</section>
</section>
+4 -4
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
+4 -4
View File
@@ -2,11 +2,11 @@
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>What is Reticulum? - Reticulum Network Stack 0.8.6 beta documentation</title>
<title>What is Reticulum? - Reticulum Network Stack 0.9.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.6 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.9.1 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.6 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.9.1 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
+207 -50
View File
@@ -27,9 +27,14 @@ and install them offline using ``pip``:
pip install ./rns-0.5.1-py3-none-any.whl
For more detailed installation instructions, please see the
:ref:`Platform-Specific Install Notes<install-guides>` section.
After installation is complete, it might be helpful to refer to the
:ref:`Using Reticulum on Your System<using-main>` chapter.
Resolving Dependency & Installation Issues
=============================================
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
On some platforms, there may not be binary packages available for all dependencies, and
``pip`` installation may fail with an error message. In these cases, the issue can usually
be resolved by installing the development essentials packages for your platform:
@@ -100,10 +105,12 @@ 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.
.. 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. In some cases, you may even need to
manually add the ``pip`` install path to your ``PATH`` environment variable.
Sideband
^^^^^^^^
@@ -305,6 +312,20 @@ you are welcome to head over to the `GitHub discussion pages <https://github.com
and propose adding an interface for the hardware.
Creating and Using Custom Interfaces
===========================================
While Reticulum includes a flexible and broad range of built-in interfaces, these
will not cover every conceivable type of communications hardware that Reticulum
can potentially use to communicate.
It is therefore possible to easily write your own interface modules, that can be
loaded at run-time and used on-par with any of the built-in interface types.
For more information on this subject, and code examples to build on, please see
the :ref:`Configuring Interfaces<interfaces-main>` chapter.
Develop a Program with Reticulum
===========================================
If you want to develop programs that use Reticulum, the easiest way to get
@@ -318,14 +339,8 @@ The above command will install Reticulum and dependencies, and you will be
ready to import and use RNS in your own programs. The next step will most
likely be to look at some :ref:`Example Programs<examples-main>`.
For extended functionality, you can install optional dependencies:
.. code::
pip install pyserial
Further information can be found in the :ref:`API Reference<api-main>`.
The entire Reticulum API is documented in the :ref:`API Reference<api-main>`
chapter of this manual.
Participate in Reticulum Development
@@ -373,6 +388,7 @@ your first pull request, it is probably a good idea to introduce yourself on
the `disucssion forum on GitHub <https://github.com/markqvist/Reticulum/discussions>`_,
or ask one of the developers or maintainers for a good place to start.
.. _install-guides:
Platform-Specific Install Notes
==============================================
@@ -451,7 +467,7 @@ here at a later point. Until then you can use the `Sideband source code <https:/
ARM64
^^^^^^^^^^^^^^^^^^^^^^^^
On some architectures, including ARM64, not all dependencies have precompiled
binaries. On such systems, you may need to install ``python3-dev`` before
binaries. On such systems, you may need to install ``python3-dev`` (or similar) before
installing Reticulum or programs that depend on Reticulum.
.. code::
@@ -463,29 +479,8 @@ installing Reticulum or programs that depend on Reticulum.
# Install Reticulum
python3 -m pip install rns
Raspberry Pi
^^^^^^^^^^^^^^^^^^^^^^^^^
It is currently recommended to use a 64-bit version of the Raspberry Pi OS
if you want to run Reticulum on Raspberry Pi computers, since 32-bit versions
don't always have packages available for some dependencies.
While it is possible to install and run Reticulum on 32-bit Rasperry Pi OSes,
it will require manually configuring and installing some packages, and is not
detailed in this manual.
OpenWRT
^^^^^^^^^^^^^^^^^^^^^^^^^
On OpenWRT systems with sufficient storage and memory, you can easily install
Reticulum and related utilities using the `opkg` package manager and `pip`.
.. code::
# Install dependencies
opkg install python3 python3-pip python3-cryptography python3-pyserial
# Install Reticulum
pip install rns
With these packages installed, ``pip`` will be able to build any missing dependencies
on your system locally.
Debian Bookworm
@@ -523,10 +518,145 @@ option, you can use the following command:
pip install rns --break-system-packages
Please note that the "break-system-packages" directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems.
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
MacOS
^^^^^^^^^^^^^^^^^^^^^^^^^
To install Reticulum on macOS, you will need to have Python and the ``pip`` package
manager installed.
Systems running macOS can vary quite widely in whether or not Python is pre-installed,
and if it is, which version is installed, and whether the ``pip`` package manager is
also installed and set up. If in doubt, you can `download and install <https://www.python.org/downloads/>`_
Python manually.
When Python and ``pip`` is available on your system, simply open a terminal window
and use one of the following commands:
.. code::
# Install Reticulum and utilities with pip:
pip3 install rns
# On some versions, you may need to use the
# flag --break-system-packages to install:
pip3 install rns --break-system-packages
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
Additionally, some version combinations of macOS and Python require you to
manually add your installed ``pip`` packages directory to your `PATH` environment
variable, before you can use installed commands in your terminal. Usually, adding
the following line to your shell init script (for example ``~/.zshrc``) will be enough:
.. code::
export PATH=$PATH:~/Library/Python/3.9/bin
Adjust Python version and shell init script location according to your system.
OpenWRT
^^^^^^^^^^^^^^^^^^^^^^^^^
On OpenWRT systems with sufficient storage and memory, you can install
Reticulum and related utilities using the `opkg` package manager and `pip`.
.. note::
At the time of releasing this manual, work is underway to create pre-built
Reticulum packages for OpenWRT, with full configuration, service
and ``uci`` integration. Please see the `feed-reticulum <https://github.com/gretel/feed-reticulum>`_
and `reticulum-openwrt <https://github.com/gretel/reticulum-openwrt>`_
repositories for more information.
To install Reticulum on OpenWRT, first log into a command line session, and
then use the following instructions:
.. code::
# Install dependencies
opkg install python3 python3-pip python3-cryptography python3-pyserial
# Install Reticulum
pip install rns
# Start rnsd with debug logging enabled
rnsd -vvv
.. note::
The above instructions have been verified and tested on OpenWRT 21.02 only.
It is likely that other versions may require slightly altered installation
commands or package names. You will also need enough free space in your
overlay FS, and enough free RAM to actually run Reticulum and any related
programs and utilities.
Depending on your device configuration, you may need to adjust firewall rules
for Reticulum connectivity to and from your device to work. Until proper
packaging is ready, you will also need to manually create a service or startup
script to automatically laucnh Reticulum at boot time.
Please also note that the `AutoInterface` requires link-local IPv6 addresses
to be enabled for any Ethernet and WiFi devices you intend to use. If ``ip a``
shows an address starting with ``fe80::`` for the device in question,
``AutoInterface`` should work for that device.
Raspberry Pi
^^^^^^^^^^^^^^^^^^^^^^^^^
It is currently recommended to use a 64-bit version of the Raspberry Pi OS
if you want to run Reticulum on Raspberry Pi computers, since 32-bit versions
don't always have packages available for some dependencies. If Python and the
`pip` package manager is not already installed, do that first, and then
install Reticulum using `pip`.
.. code::
# Install dependencies
sudo apt install python3 python3-pip python3-cryptography python3-pyserial
# Install Reticulum
pip install rns --break-system-packages
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
While it is possible to install and run Reticulum on 32-bit Rasperry Pi OSes,
it will require manually configuring and installing required build dependencies,
and is not detailed in this manual.
RISC-V
^^^^^^^^^^^^^^^^^^^^^^^^
On some architectures, including RISC-V, not all dependencies have precompiled
binaries. On such systems, you may need to install ``python3-dev`` (or similar) before
installing Reticulum or programs that depend on Reticulum.
.. code::
# Install Python and development packages
sudo apt update
sudo apt install python3 python3-pip python3-dev
# Install Reticulum
python3 -m pip install rns
With these packages installed, ``pip`` will be able to build any missing dependencies
on your system locally.
Ubuntu Lunar
@@ -564,13 +694,45 @@ option, you can use the following command:
pip install rns --break-system-packages
Please note that the "break-system-packages" directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this _could_ in rare
cases lead to version conflicts, it does not generally pose any problems.
.. note::
The ``--break-system-packages`` directive is a somewhat misleading choice
of words. Setting it will of course not break any system packages, but will simply
allow installing ``pip`` packages user- and system-wide. While this *could* in rare
cases lead to version conflicts, it does not generally pose any problems, especially
not in the case of installing Reticulum.
Windows
^^^^^^^^^^^^^^^^^^^^^^^^^
On Windows operating systems, the easiest way to install Reticulum is by using the
``pip`` package manager from the command line (either the command prompt or Windows
Powershell).
If you don't already have Python installed, `download and install Python <https://www.python.org/downloads/>`_.
At the time of publication of this manual, the recommended version is `Python 3.12.7 <https://www.python.org/downloads/release/python-3127>`_.
**Important!** When asked by the installer, make sure to add the Python program to
your PATH environment variables. If you don't do this, you will not be able to
use the ``pip`` installer, or run the included Reticulum utility programs (such as
``rnsd`` and ``rnstatus``) from the command line.
After installing Python, open the command prompt or Windows Powershell, and type:
.. code::
pip install rns
You can now use Reticulum and all included utility programs directly from your
preferred command line interface.
Pure-Python Reticulum
==============================================
.. warning::
If you use the ``rnspure`` package to run Reticulum on systems that
do not support `PyCA/cryptography <https://github.com/pyca/cryptography>`_, it is
important that you read and understand the :ref:`Cryptographic Primitives <understanding-primitives>`
section of this manual.
In some rare cases, and on more obscure system types, it is not possible to
install one or more dependencies. In such situations,
you can use the ``rnspure`` package instead of the ``rns`` package, or use ``pip``
@@ -585,8 +747,3 @@ only if they are *needed* and *available*. If for example you want to use Reticu
on a system that cannot support ``pyserial``, it is perfectly possible to do so using
the `rnspure` package, but Reticulum will not be able to use serial-based interfaces.
All other available modules will still be loaded when needed.
**Please Note!** If you use the `rnspure` package to run Reticulum on systems that
do not support `PyCA/cryptography <https://github.com/pyca/cryptography>`_, it is
important that you read and understand the :ref:`Cryptographic Primitives <understanding-primitives>`
section of this manual.
+4 -4
View File
@@ -75,8 +75,8 @@ completely from scratch, to your exact desired specifications, this chapter
will explain the easiest possible approach to creating RNodes: Using common
LoRa development boards. This approach can be boiled down to two simple steps:
1. Obtain one or more supported development boards
2. Install the RNode firmware with the automated installer
1. Obtain one or more :ref:`supported development boards<rnode-supported>`
2. Install the RNode firmware with the :ref:`automated installer<rnode-installation>`
Once the firmware has been installed and provisioned by the install script, it
is ready to use with any software that supports RNodes, including Reticulum.
@@ -85,8 +85,8 @@ to the configuration.
.. _rnode-supported:
Supported Boards
^^^^^^^^^^^^^^^^
Supported Boards and Devices
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To create one or more RNodes, you will need to obtain supported development
boards. The following boards are supported by the auto-installer.
+38 -14
View File
@@ -162,11 +162,12 @@ 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.
.. 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
@@ -238,8 +239,9 @@ can simply specify the Yggdrasil ``tun`` device and a listening port, like so:
device = tun0
listen_port = 4343
**Please Note!** The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:
.. note::
The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:
.. code::
@@ -311,8 +313,9 @@ 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:
.. note::
The TCP interfaces support tunneling over I2P, but to do so reliably,
you must use the i2p_tunneled option:
.. code::
@@ -334,11 +337,12 @@ private and the internet. It can also allow broadcast communication
over IP networks, so it can provide an easy way to enable connectivity
with all other peers on a local area network.
*Please Note!* Using broadcast UDP traffic has performance implications,
especially on WiFi. If your goal is simply to enable easy communication
with all peers in your local Ethernet broadcast domain, the
:ref:`Auto Interface<interfaces-auto>` performs better, and is even
easier to use.
.. warning::
Using broadcast UDP traffic has performance implications,
especially on WiFi. If your goal is simply to enable easy communication
with all peers in your local Ethernet broadcast domain, the
:ref:`Auto Interface<interfaces-auto>` performs better, and is even
easier to use.
.. code::
@@ -393,6 +397,11 @@ RNode LoRa Interface
To use Reticulum over LoRa, the `RNode <https://unsigned.io/rnode/>`_ interface
can be used, and offers full control over LoRa parameters.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
# Here's an example of how to add a LoRa interface
@@ -480,6 +489,11 @@ RNode Multi Interface
For RNodes that support multiple LoRa transceivers, the RNode
Multi interface can be used to configure sub-interfaces individually.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
# Here's an example of how to add an RNode Multi interface
@@ -647,6 +661,11 @@ radio modems and TNCs, including `OpenModem <https://unsigned.io/openmodem/>`_.
KISS interfaces can also be configured to periodically send out beacons
for station identification purposes.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
[[Packet Radio KISS Interface]]
@@ -710,6 +729,11 @@ encapsulate in AX.25.
A more efficient way is to use the plain KISS interface with the
beaconing functionality described above.
.. warning::
Radio frequency spectrum is a legally controlled resource, and legislation
varies widely around the world. It is your responsibility to be aware of any
relevant regulation for your location, and to make decisions accordingly.
.. code::
[[Packet Radio AX.25 KISS Interface]]
+3 -1
View File
@@ -32,7 +32,9 @@ Provide Feedback
================
All feedback on the usage, functioning and potential dysfunctioning of any and
all components of the system is very valuable to the continued development and
improvement of Reticulum. Absolutely no automated analytics, telemetry, error
improvement of Reticulum.
Absolutely no automated analytics, telemetry, error
reporting or statistics is collected and reported by Reticulum under any
circumstances, so we rely on old-fashioned human feedback.
+4 -3
View File
@@ -904,6 +904,7 @@ with the OpenSSL backend being *much* faster. The most important consequence how
potential loss of security by using primitives that has not seen the same amount of scrutiny,
testing and review as those from OpenSSL.
If you want to use the internal pure-python primitives, it is **highly advisable** that you
have a good understanding of the risks that this pose, and make an informed decision on whether
those risks are acceptable to you.
.. warning::
If you want to use the internal pure-python primitives, it is **highly advisable** that you
have a good understanding of the risks that this pose, and make an informed decision on whether
those risks are acceptable to you.
+1 -1
View File
@@ -189,4 +189,4 @@ such. While it has been built with cryptography best-practices very foremost in
mind, it has not yet been externally security audited, and there could very well be
privacy-breaking bugs. To be considered secure, Reticulum needs a thorough
security review by independent cryptographers and security researchers. If you
want to help out with this, or can help sponsor an audit, please do get in touch.
want to help out with this, or can help sponsor an audit, please do get in touch.
+2 -2
View File
@@ -333,7 +333,7 @@ class TestLink(unittest.TestCase):
resource_size = 5*1000*1000
data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout)
resource = RNS.Resource(data, l1, timeout=resource_timeout, auto_compress=False)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
@@ -380,7 +380,7 @@ class TestLink(unittest.TestCase):
resource_size = 50*1000*1000
data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback, auto_compress=False)
start = time.time()
TestLink.large_resource_status = resource.status