mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-07-19 22:48:10 -07:00
Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0da489156 | |||
| 2dcf1350e7 | |||
| 1e280611ce | |||
| f1d107846f | |||
| cc951dcb53 | |||
| b5856a3706 | |||
| ed3479da9a | |||
| 5e15f421b7 | |||
| 0a9366ba6e | |||
| cf31435f39 | |||
| 9f58860842 | |||
| 875348383d | |||
| f79f190525 | |||
| 5e27a81412 | |||
| 0dcb009579 | |||
| 943f76804b | |||
| 8bbe6ae3ae | |||
| f0d85dd078 | |||
| f85dda1829 | |||
| 91e064cdf1 | |||
| fb4e53f6e3 | |||
| 03340ed091 | |||
| ed424fa0a2 | |||
| 406ab216d1 | |||
| 00d8a2064d | |||
| 38b920e393 | |||
| 1ed000c4d9 | |||
| d360958d10 | |||
| fcdb455d73 | |||
| 575639b721 | |||
| 492573f9fe | |||
| c5d30f8ee6 | |||
| 3c4791a622 | |||
| 803a5736c9 | |||
| 267ffbdf5f | |||
| 52028aa44c | |||
| c5248d53d6 | |||
| 2d2f0947ac | |||
| 4fa616a326 | |||
| 136713eec1 | |||
| 0fd75cb819 | |||
| ea52153969 | |||
| 3854781028 | |||
| ec2805f357 | |||
| b5cb3a65dd | |||
| c79cb3aa20 | |||
| 8bff119691 | |||
| 5e0b2c5b42 | |||
| 8908022b88 | |||
| b0dda0ed86 | |||
| 6ae72d4225 | |||
| 0a188a2d39 | |||
| 036abb28fe | |||
| a732767a28 | |||
| 32a1261d98 | |||
| 27c5af3bbc | |||
| 5872108da3 | |||
| 8f6c6b76de | |||
| 99db625c62 | |||
| fdf6a31cbd | |||
| 75f353d7e2 | |||
| 82f204fb44 | |||
| 8d4492ecfd | |||
| f8a53458d6 | |||
| 4229837170 | |||
| 4be2ae6c70 | |||
| dbdeba2fe0 | |||
| 7e34b61f37 | |||
| bf726ed2c7 | |||
| fa54a2affe | |||
| 62e1d0e554 | |||
| 9c823a038b | |||
| 1e6cd50f46 | |||
| 06716e4873 | |||
| 8e4a1e3ffa | |||
| 0abb3bd4c3 | |||
| 336574daed | |||
| 07938ba111 | |||
| e699eb6d25 | |||
| 3864549752 | |||
| 0b934cd0f6 | |||
| 5bac38a752 | |||
| 72c8d4d3dd | |||
| b8c6ea015e | |||
| ffe1beb7ae | |||
| 21c6dbfce0 | |||
| 70cbb8dc79 | |||
| 334f2a364d | |||
| b477354235 | |||
| 254c966159 | |||
| 7ee9b07d9c | |||
| 839b72469c | |||
| 874d76b343 | |||
| 7497e7aa0c | |||
| efa084fb0f | |||
| 48e4a27054 | |||
| 96cf6a790e | |||
| d7b54ff397 | |||
| 90ab065073 | |||
| b6f0784311 | |||
| e37ec654ee | |||
| b237d51276 | |||
| 155ea24008 | |||
| 8c8affc800 | |||
| 481062fca1 | |||
| ffcc5560dc | |||
| 09e146ef0b | |||
| 4c6b04ff69 | |||
| 9889b479d1 | |||
| 95dec00c76 | |||
| cff268926d | |||
| 6fa88f4e4a | |||
| ab8e6791fe | |||
| 13c45cc59a | |||
| 67c468884f | |||
| f028d44609 | |||
| 18b952e612 | |||
| 25178d8f50 | |||
| 1c0b7c00fd | |||
| 2439761529 | |||
| 8803dd5b65 | |||
| d15d04eae5 | |||
| bf40f74a4a | |||
| c0339c0f46 | |||
| b64bb166c0 | |||
| 31d30030dc | |||
| 556e111a98 | |||
| 70b0dd621b | |||
| f7d3212651 | |||
| 0a29f0cfa1 | |||
| 97153ad59d | |||
| bc8378fb60 | |||
| 3320cf8da8 | |||
| bb53bd3f27 | |||
| 73eed59fab | |||
| 91ede52634 | |||
| 93f13a98b2 | |||
| c87c5c9709 | |||
| b0c6c53430 | |||
| 94a5222390 | |||
| 98bb304060 | |||
| 08bfd923ea | |||
| ae28f04ce4 | |||
| 024a742f2a | |||
| df184f3e54 | |||
| 5542410afa | |||
| 99205cdc0f | |||
| 8c936af963 | |||
| 7fe751e74f | |||
| 6d551578c3 | |||
| 40c85fb607 | |||
| 743736b376 | |||
| 7fdb431d70 | |||
| ebcc3d8912 | |||
| 32e29a54c3 | |||
| 049733c4b6 | |||
| 420d58527d | |||
| bab779a34c | |||
| 45aa71b2b7 | |||
| 6dcfe2cad6 | |||
| f206047908 | |||
| 6ce979a7de | |||
| 97f97eb063 | |||
| f3db762e9f | |||
| f9f623dfa5 | |||
| ffa6bec3b4 | |||
| 4f78973751 | |||
| a8a7af4b74 | |||
| 45295c779c | |||
| a82376d1f5 | |||
| 75c6248264 | |||
| 9294ab4f97 | |||
| f01193e854 | |||
| d7375bc4c3 | |||
| 1a860c6ffd | |||
| 800ed3af7a | |||
| 9c8e79546c | |||
| 4c272aa536 | |||
| e184861822 | |||
| d40e19f08d |
+1
-1
@@ -10,6 +10,6 @@ docs/build
|
|||||||
rns*.egg-info
|
rns*.egg-info
|
||||||
profile.data
|
profile.data
|
||||||
tests/rnsconfig/storage
|
tests/rnsconfig/storage
|
||||||
tests/rnsconfig/logfile
|
tests/rnsconfig/logfile*
|
||||||
*.data
|
*.data
|
||||||
*.result
|
*.result
|
||||||
|
|||||||
+179
@@ -1,3 +1,182 @@
|
|||||||
|
### 2023-10-07: RNS β 0.6.2
|
||||||
|
|
||||||
|
This maintenance release adds the ability to specify the shared instance RPC key in the Reticulum file, making it possible to use all Reticulum functionality in the terminal on Android.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added configuration option to specify shared instance RPC key
|
||||||
|
- Reordered airtime stats in `rnstatus`
|
||||||
|
- Updated log levels on Android
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Adding missing superclass init on Android interfaces
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
a9958ad90f34f344003e18077f7abd3fa85666a39dc0cae8580071820dee13f9 rns-0.6.2-py3-none-any.whl
|
||||||
|
e68e8837d35d1a07a82c4b0e9db50ceace737a650e6e7e9ce2d9a013fd28f529 rnspure-0.6.2-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-10-01: RNS β 0.6.1
|
||||||
|
|
||||||
|
This release brings a number of bugfixes, along with useful new mechanisms for ensuring network stability under high, non-constructive and unusual announce load situation.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added announce ingress rate control for new and unknown destinations
|
||||||
|
- Added per-interface announce frequency monitoring to the transport engine
|
||||||
|
- Added per-interface announce burst hold queues
|
||||||
|
- Added announce frequency statistics to `rnstatus`
|
||||||
|
- Added option to sort `rnstatus` output according to various metrics
|
||||||
|
- Added timeout options to `rnprobe`
|
||||||
|
- Added ability to drop all paths via a specific transport instance to `rnpath`
|
||||||
|
- Added new options and features to documentation and manual
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed announce queue not clearing all announces with exceeded retry limit at the same time
|
||||||
|
- Fixed a bug that caused local packet RSSI and SNR cache to get stuck
|
||||||
|
- Fixed output formatting in `rncp`
|
||||||
|
- Fixed `rnid` not allowing single-aspect destination names
|
||||||
|
- Fixed a number of typos in the documentation
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
461e5cafa7560dcd3ec047141d10f0f48f151c36e1af1d65ec6c65f732cea46a rns-0.6.1-py3-none-any.whl
|
||||||
|
be6a4a6069f2d050e21582f2cf9d3bb59ed4040a0f07761a540bd752d90ea591 rnspure-0.6.1-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2023-09-21: RNS β 0.6.0
|
||||||
|
|
||||||
|
This release brings a few performance improvements, additions to the included utilities, and fixes a number of bugs.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added ability to run automatic probe responder on Transport Instances
|
||||||
|
- Improved `rnprobe` utility
|
||||||
|
- Improved AutoInterface peering on Android devices
|
||||||
|
- Improved Transport performance
|
||||||
|
- Improved path re-discovery when local nodes roam to other network segments
|
||||||
|
- Updated various parts of the documentation
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed missing timeout check in `rncp`
|
||||||
|
- Fixed missing link status check on `Identify()` call, which could lead to an unnecessary exception
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
88a26b1593e82a628dab96dbe8820548aea0159235f730fa992bf1833db59246 rns-0.6.0-py3-none-any.whl
|
||||||
|
bcee416e4fb52346d01f6e0c46b1cebf84b127cc516603367fc2ae00a4149fa2 rnspure-0.6.0-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-09-19: RNS β 0.5.9
|
||||||
|
|
||||||
|
This release brings major efficiency improvements to `Channel` and `Buffer` classes, adds a range of usability improvements to the included utilities and fixes a number of bugs.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Improved `Channel` sequencing, retries and transfer efficiency
|
||||||
|
- Added adaptive compression to `Buffer` class
|
||||||
|
- Added `rnid` examples and documentation to manual
|
||||||
|
- Added silent mode to `rncp`
|
||||||
|
- Added remote fetch mode to `rncp`
|
||||||
|
- Added allowed_identities file support to `rncp`
|
||||||
|
- Added Transport Instance uptime to `rnstatus` output
|
||||||
|
- Added channel CSMA parameter stats to RNode Interface `rnstatus` output
|
||||||
|
- Added ability to set custom RNode OLED display address with rnodeconf
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed inadverdent AutoInterface multi-IF deque hit for resource transfer retries
|
||||||
|
- Fixed invalid path for firmware hash generation while using extracted firmware to autoinstall in `rnodeconf`
|
||||||
|
- Fixed various minor missing error checks
|
||||||
|
- Fixed `rnid` status output bug
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
207ab20bd68bab16b417fbd41a4ecdbcf1e2f6fa553d48df6c8fc181b6e84dac rns-0.5.9-py3-none-any.whl
|
||||||
|
93f0965567dfc2c43f3d703481fe1a7d7b1b8d0b3837ad41c37f28a8af5c1acc rnspure-0.5.9-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-09-14: RNS β 0.5.8
|
||||||
|
|
||||||
|
This maintenance release contains a number of usability improvements to Reticulum and related tools.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Various documentation updates
|
||||||
|
- Improved path-resolution in mixed networks with roaming-mode nodes
|
||||||
|
- Added channel load and airtime stats to `rnstatus` output
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
27ba5cdc4724fc8c7211c3b504f097f6adf47f7b80775e6297e4c4e621ef6348 rns-0.5.8-py3-none-any.whl
|
||||||
|
1ea1c949763c9478ec48f064f7f7864d9f859101ab91b44400879371f490800f rnspure-0.5.8-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-08-14: RNS β 0.5.7
|
||||||
|
|
||||||
|
This maintenance release contains a number of bugfixes and quality improvements to Reticulum and related tools.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added bytes input to destination hash convenience functions
|
||||||
|
- Fixed possible invalid comparison in link watchdog job
|
||||||
|
- Add option to `rnodeconf` to set baud rate when flashing
|
||||||
|
- Added better explanation in `rnodeconf` when flashing fails
|
||||||
|
- Fixed EEPROM dump directory in `rnodeconf`
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
867fbb5c73c2a49a75e1f8f3e9f376b507b683328e26c64d4387acd0cc1dbbc7 rns-0.5.7-py3-none-any.whl
|
||||||
|
7bab2865264b32208e023b5c4bbe88c37f51e3176ca4a8cf332d95f59a6d7f2c rnspure-0.5.7-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-07-09: RNS β 0.5.6
|
||||||
|
|
||||||
|
This maintenance release contains a few bugfixes.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Fixed an issue in `rnodeconf` that prevented Heltec LoRa32 v2 boards from being flashed.
|
||||||
|
- Fixed a typo in the `rnid` utility.
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
255a5b4bac28326c6b2cc85f43b26dcb0606404a4abd2dfa8244937155838973 rns-0.5.6-py3-none-any.whl
|
||||||
|
1510b6da4641ceaa4c599a142e498c7e2c1ae12035868f9db1c111e5600161e9 rnspure-0.5.6-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-06-13: RNS β 0.5.5
|
||||||
|
|
||||||
|
This maintenance release brings a single bugfix.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Fixed a race condition for link initiators on timed out link establishments.
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
4ae61d28bf981a7cb853c179e9de3b56b350d2dc984fb671a21d38c4ce5b449e rns-0.5.5-py3-none-any.whl
|
||||||
|
ed417cbd3c90e9f1b68565a3411ca5c9bc936b495300fd1ace3c4a6414aabd5a rnspure-0.5.5-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-05-19: RNS β 0.5.4
|
||||||
|
|
||||||
|
This maintenance release brings a single bugfix.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Fixed a potential race condition when timed-out link receives a late establishment proof a few milliseconds after it has timed out.
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
71b42fe737da97a4b63bb227c29bb67854a7f003c9585f085b0ff68c8f460815 rns-0.5.4-py3-none-any.whl
|
||||||
|
af6949d581445444f57cfca75756200e7c509a6fc66483d859716ce6a06064db rnspure-0.5.4-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-05-19: RNS β 0.5.3
|
||||||
|
|
||||||
|
This maintenance release brings a single, but important bugfix.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Fixed a bug that could cause data corruption to occur over when using `Buffer` instances.
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
f23c8d655c9e80a12a6728495aec56f19f27184d3d8e6b6ed6184b0e89d4be35 rns-0.5.3-py3-none-any.whl
|
||||||
|
2c692a2153bb766a9dc2391340a06f429c13a75b86b746b69c6fcd5a4fe5ee33 rnspure-0.5.3-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
### 2023-05-12: RNS β 0.5.2
|
### 2023-05-12: RNS β 0.5.2
|
||||||
|
|
||||||
This maintenance release brings a number of bugfixes and improvements.
|
This maintenance release brings a number of bugfixes and improvements.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def program_setup(configpath):
|
|||||||
# Destinations are endpoints in Reticulum, that can be addressed
|
# Destinations are endpoints in Reticulum, that can be addressed
|
||||||
# and communicated with. Destinations can also announce their
|
# and communicated with. Destinations can also announce their
|
||||||
# existence, which will let the network know they are reachable
|
# existence, which will let the network know they are reachable
|
||||||
# and autoomatically create paths to them, from anywhere else
|
# and automatically create paths to them, from anywhere else
|
||||||
# in the network.
|
# in the network.
|
||||||
destination_1 = RNS.Destination(
|
destination_1 = RNS.Destination(
|
||||||
identity,
|
identity,
|
||||||
@@ -53,7 +53,7 @@ def program_setup(configpath):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# We configure the destinations to automatically prove all
|
# We configure the destinations to automatically prove all
|
||||||
# packets adressed to it. By doing this, RNS will automatically
|
# packets addressed to it. By doing this, RNS will automatically
|
||||||
# generate a proof for each incoming packet and transmit it
|
# generate a proof for each incoming packet and transmit it
|
||||||
# back to the sender of that packet. This will let anyone that
|
# back to the sender of that packet. This will let anyone that
|
||||||
# tries to communicate with the destination know whether their
|
# tries to communicate with the destination know whether their
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ def server(configpath):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# We configure the destination to automatically prove all
|
# We configure the destination to automatically prove all
|
||||||
# packets adressed to it. By doing this, RNS will automatically
|
# packets addressed to it. By doing this, RNS will automatically
|
||||||
# generate a proof for each incoming packet and transmit it
|
# generate a proof for each incoming packet and transmit it
|
||||||
# back to the sender of that packet.
|
# back to the sender of that packet.
|
||||||
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@ def program_setup(configpath):
|
|||||||
# Destinations are endpoints in Reticulum, that can be addressed
|
# Destinations are endpoints in Reticulum, that can be addressed
|
||||||
# and communicated with. Destinations can also announce their
|
# and communicated with. Destinations can also announce their
|
||||||
# existence, which will let the network know they are reachable
|
# existence, which will let the network know they are reachable
|
||||||
# and autoomatically create paths to them, from anywhere else
|
# and automatically create paths to them, from anywhere else
|
||||||
# in the network.
|
# in the network.
|
||||||
destination = RNS.Destination(
|
destination = RNS.Destination(
|
||||||
identity,
|
identity,
|
||||||
@@ -36,7 +36,7 @@ def program_setup(configpath):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# We configure the destination to automatically prove all
|
# We configure the destination to automatically prove all
|
||||||
# packets adressed to it. By doing this, RNS will automatically
|
# packets addressed to it. By doing this, RNS will automatically
|
||||||
# generate a proof for each incoming packet and transmit it
|
# generate a proof for each incoming packet and transmit it
|
||||||
# back to the sender of that packet. This will let anyone that
|
# back to the sender of that packet. This will let anyone that
|
||||||
# tries to communicate with the destination know whether their
|
# tries to communicate with the destination know whether their
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ userland, and can run on practically any system that runs Python 3.
|
|||||||
## Read The Manual
|
## Read The Manual
|
||||||
The full documentation for Reticulum is available at [markqvist.github.io/Reticulum/manual/](https://markqvist.github.io/Reticulum/manual/).
|
The full documentation for Reticulum is available at [markqvist.github.io/Reticulum/manual/](https://markqvist.github.io/Reticulum/manual/).
|
||||||
|
|
||||||
You can also [download the Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf)
|
You can also download the [Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf) or [as an e-book in EPUB format](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.epub).
|
||||||
|
|
||||||
For more info, see [reticulum.network](https://reticulum.network/)
|
For more info, see [reticulum.network](https://reticulum.network/)
|
||||||
|
|
||||||
@@ -59,11 +59,13 @@ For more info, see [reticulum.network](https://reticulum.network/)
|
|||||||
- The API is very easy to use, and provides transfer progress
|
- The API is very easy to use, and provides transfer progress
|
||||||
- Lightweight, flexible and expandable Request/Response mechanism
|
- Lightweight, flexible and expandable Request/Response mechanism
|
||||||
- Efficient link establishment
|
- Efficient link establishment
|
||||||
- Total bandwidth cost of setting up an encrypted link is 3 packets totaling 297 bytes
|
- Total cost of setting up an encrypted and verified link is only 3 packets, totalling 297 bytes
|
||||||
- Low cost of keeping links open at only 0.44 bits per second
|
- Low cost of keeping links open at only 0.44 bits per second
|
||||||
|
- Reliable sequential delivery with Channel and Buffer mechanisms
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
While Reticulum is already a fully featured and functional networking stack, many improvements and additions are actively being worked on, and planned for the future.
|
While Reticulum is already a fully featured and functional networking stack,
|
||||||
|
many improvements and additions are actively being worked on, and planned for the future.
|
||||||
|
|
||||||
To learn more about the direction and future of Reticulum, please see the [Development Roadmap](./Roadmap.md).
|
To learn more about the direction and future of Reticulum, please see the [Development Roadmap](./Roadmap.md).
|
||||||
|
|
||||||
@@ -108,14 +110,28 @@ you want to do. For full details and examples, have a look at the
|
|||||||
[Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html)
|
[Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html)
|
||||||
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
||||||
|
|
||||||
To simply install Reticulum and related utilities on your system, the easiest way is via pip:
|
To simply install Reticulum and related utilities on your system, the easiest way is via `pip`.
|
||||||
|
You can then start any program that uses Reticulum, or start Reticulum as a system service with
|
||||||
|
[the rnsd utility](https://markqvist.github.io/Reticulum/manual/using.html#the-rnsd-utility).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install rns
|
pip install rns
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then start any program that uses Reticulum, or start Reticulum as a
|
If you are using an operating system that blocks normal user package installation via `pip`,
|
||||||
system service with [the rnsd utility](https://markqvist.github.io/Reticulum/manual/using.html#the-rnsd-utility).
|
you can return `pip` to normal behaviour by editing the `~/.config/pip/pip.conf` file,
|
||||||
|
and adding the following directive in the `[global]` section:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[global]
|
||||||
|
break-system-packages = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use the `pipx` tool to install Reticulum in an isolated environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pipx install rns
|
||||||
|
```
|
||||||
|
|
||||||
When first started, Reticulum will create a default configuration file,
|
When first started, Reticulum will create a default configuration file,
|
||||||
providing basic connectivity to other Reticulum peers that might be locally
|
providing basic connectivity to other Reticulum peers that might be locally
|
||||||
@@ -139,12 +155,15 @@ section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/)
|
|||||||
- An interface status utility called `rnstatus`, that displays information about interfaces
|
- An interface status utility called `rnstatus`, that displays information about interfaces
|
||||||
- The path lookup and management tool `rnpath` letting you view and modify path tables
|
- The path lookup and management tool `rnpath` letting you view and modify path tables
|
||||||
- A diagnostics tool called `rnprobe` for checking connectivity to destinations
|
- A diagnostics tool called `rnprobe` for checking connectivity to destinations
|
||||||
- A simple file transfer program called `rncp` making it easy to copy files to remote systems
|
- A simple file transfer program called `rncp` making it easy to transfer files between systems
|
||||||
|
- The identity management and encryption utility `rnid` let's you manage Identities and encrypt/decrypt files
|
||||||
- The remote command execution program `rnx` let's you run commands and
|
- The remote command execution program `rnx` let's you run commands and
|
||||||
programs and retrieve output from remote systems
|
programs and retrieve output from remote systems
|
||||||
|
|
||||||
All tools, including `rnx` and `rncp`, work reliably and well even over very
|
All tools, including `rnx` and `rncp`, work reliably and well even over very
|
||||||
low-bandwidth links like LoRa or Packet Radio.
|
low-bandwidth links like LoRa or Packet Radio. For full-featured remote shells
|
||||||
|
over Reticulum, also have a look at the [rnsh](https://github.com/acehoss/rnsh)
|
||||||
|
program.
|
||||||
|
|
||||||
## Supported interface types and devices
|
## Supported interface types and devices
|
||||||
|
|
||||||
@@ -227,11 +246,11 @@ I2P. Just add one of the following interfaces to your Reticulum configuration
|
|||||||
file:
|
file:
|
||||||
|
|
||||||
```
|
```
|
||||||
# TCP/IP interface to the RNS Dublin Hub
|
# TCP/IP interface to the RNS Amsterdam Hub
|
||||||
[[RNS Testnet Dublin]]
|
[[RNS Testnet Amsterdam]]
|
||||||
type = TCPClientInterface
|
type = TCPClientInterface
|
||||||
enabled = yes
|
enabled = yes
|
||||||
target_host = dublin.connect.reticulum.network
|
target_host = amsterdam.connect.reticulum.network
|
||||||
target_port = 4965
|
target_port = 4965
|
||||||
|
|
||||||
# TCP/IP interface to the BetweenTheBorders Hub (community-provided)
|
# TCP/IP interface to the BetweenTheBorders Hub (community-provided)
|
||||||
@@ -245,7 +264,7 @@ file:
|
|||||||
[[RNS Testnet I2P Hub]]
|
[[RNS Testnet I2P Hub]]
|
||||||
type = I2PInterface
|
type = I2PInterface
|
||||||
enabled = yes
|
enabled = yes
|
||||||
peers = pmlm3l5rpympihoy2o5ago43kluei2jjjzsalcuiuylbve3mwi2a.b32.i2p
|
peers = g3br23bvx3lq5uddcsjii74xgmn6y5q325ovrkq2zw2wbzbqgbuq.b32.i2p
|
||||||
```
|
```
|
||||||
|
|
||||||
The testnet also contains a number of [Nomad Network](https://github.com/markqvist/nomadnet) nodes, and LXMF propagation nodes.
|
The testnet also contains a number of [Nomad Network](https://github.com/markqvist/nomadnet) nodes, and LXMF propagation nodes.
|
||||||
|
|||||||
+70
-14
@@ -1,6 +1,29 @@
|
|||||||
|
# MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import bz2
|
import bz2
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import threading
|
import threading
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
import struct
|
import struct
|
||||||
@@ -28,7 +51,7 @@ class StreamDataMessage(MessageBase):
|
|||||||
calculcated based on the value of OVERHEAD
|
calculcated based on the value of OVERHEAD
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, stream_id: int = None, data: bytes = None, eof: bool = False):
|
def __init__(self, stream_id: int = None, data: bytes = None, eof: bool = False, compressed: bool = False):
|
||||||
"""
|
"""
|
||||||
This class is used to encapsulate binary stream
|
This class is used to encapsulate binary stream
|
||||||
data to be sent over a ``Channel``.
|
data to be sent over a ``Channel``.
|
||||||
@@ -41,7 +64,7 @@ class StreamDataMessage(MessageBase):
|
|||||||
if stream_id is not None and stream_id > self.STREAM_ID_MAX:
|
if stream_id is not None and stream_id > self.STREAM_ID_MAX:
|
||||||
raise ValueError("stream_id must be 0-16383")
|
raise ValueError("stream_id must be 0-16383")
|
||||||
self.stream_id = stream_id
|
self.stream_id = stream_id
|
||||||
self.compressed = False
|
self.compressed = compressed
|
||||||
self.data = data or bytes()
|
self.data = data or bytes()
|
||||||
self.eof = eof
|
self.eof = eof
|
||||||
|
|
||||||
@@ -49,13 +72,6 @@ class StreamDataMessage(MessageBase):
|
|||||||
if self.stream_id is None:
|
if self.stream_id is None:
|
||||||
raise ValueError("stream_id")
|
raise ValueError("stream_id")
|
||||||
|
|
||||||
compressed_data = bz2.compress(self.data)
|
|
||||||
saved = len(self.data)-len(compressed_data)
|
|
||||||
|
|
||||||
if saved > 0:
|
|
||||||
self.data = compressed_data
|
|
||||||
self.compressed = True
|
|
||||||
|
|
||||||
header_val = (0x3fff & self.stream_id) | (0x8000 if self.eof else 0x0000) | (0x4000 if self.compressed > 0 else 0x0000)
|
header_val = (0x3fff & self.stream_id) | (0x8000 if self.eof else 0x0000) | (0x4000 if self.compressed > 0 else 0x0000)
|
||||||
return bytes(struct.pack(">H", header_val) + (self.data if self.data else bytes()))
|
return bytes(struct.pack(">H", header_val) + (self.data if self.data else bytes()))
|
||||||
|
|
||||||
@@ -65,6 +81,7 @@ class StreamDataMessage(MessageBase):
|
|||||||
self.compressed = (0x4000 & self.stream_id) > 0
|
self.compressed = (0x4000 & self.stream_id) > 0
|
||||||
self.stream_id = self.stream_id & 0x3fff
|
self.stream_id = self.stream_id & 0x3fff
|
||||||
self.data = raw[2:]
|
self.data = raw[2:]
|
||||||
|
|
||||||
if self.compressed:
|
if self.compressed:
|
||||||
self.data = bz2.decompress(self.data)
|
self.data = bz2.decompress(self.data)
|
||||||
|
|
||||||
@@ -129,9 +146,9 @@ class RawChannelReader(RawIOBase, AbstractContextManager):
|
|||||||
self._eof = True
|
self._eof = True
|
||||||
for listener in self._listeners:
|
for listener in self._listeners:
|
||||||
try:
|
try:
|
||||||
listener(len(self._buffer))
|
threading.Thread(target=listener, name="Message Callback", args=[len(self._buffer)], daemon=True).start()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
RNS.log("Error calling RawChannelReader(" + str(self._stream_id) + ") callback: " + str(ex))
|
RNS.log("Error calling RawChannelReader(" + str(self._stream_id) + ") callback: " + str(ex), RNS.LOG_ERROR)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -184,6 +201,10 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
|
|||||||
object, see the Python documentation for
|
object, see the Python documentation for
|
||||||
``RawIOBase``.
|
``RawIOBase``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
MAX_CHUNK_LEN = 1024*16
|
||||||
|
COMPRESSION_TRIES = 4
|
||||||
|
|
||||||
def __init__(self, stream_id: int, channel: Channel):
|
def __init__(self, stream_id: int, channel: Channel):
|
||||||
"""
|
"""
|
||||||
Create a raw channel writer.
|
Create a raw channel writer.
|
||||||
@@ -197,16 +218,51 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
|
|||||||
|
|
||||||
def write(self, __b: bytes) -> int | None:
|
def write(self, __b: bytes) -> int | None:
|
||||||
try:
|
try:
|
||||||
chunk = bytes(__b[:StreamDataMessage.MAX_DATA_LEN])
|
comp_tries = RawChannelWriter.COMPRESSION_TRIES
|
||||||
message = StreamDataMessage(self._stream_id, chunk, self._eof)
|
comp_try = 1
|
||||||
|
comp_success = False
|
||||||
|
chunk_len = len(__b)
|
||||||
|
if chunk_len > RawChannelWriter.MAX_CHUNK_LEN:
|
||||||
|
chunk_len = RawChannelWriter.MAX_CHUNK_LEN
|
||||||
|
__b = __b[:RawChannelWriter.MAX_CHUNK_LEN]
|
||||||
|
chunk_segment = None
|
||||||
|
while chunk_len > 32 and comp_try < comp_tries:
|
||||||
|
chunk_segment_length = int(chunk_len/comp_try)
|
||||||
|
compressed_chunk = bz2.compress(__b[:chunk_segment_length])
|
||||||
|
compressed_length = len(compressed_chunk)
|
||||||
|
if compressed_length < StreamDataMessage.MAX_DATA_LEN and compressed_length < chunk_segment_length:
|
||||||
|
comp_success = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
comp_try += 1
|
||||||
|
|
||||||
|
if comp_success:
|
||||||
|
chunk = compressed_chunk
|
||||||
|
processed_length = chunk_segment_length
|
||||||
|
else:
|
||||||
|
chunk = bytes(__b[:StreamDataMessage.MAX_DATA_LEN])
|
||||||
|
processed_length = len(chunk)
|
||||||
|
|
||||||
|
message = StreamDataMessage(self._stream_id, chunk, self._eof, comp_success)
|
||||||
|
|
||||||
self._channel.send(message)
|
self._channel.send(message)
|
||||||
return len(chunk)
|
return processed_length
|
||||||
|
|
||||||
except RNS.Channel.ChannelException as cex:
|
except RNS.Channel.ChannelException as cex:
|
||||||
if cex.type != RNS.Channel.CEType.ME_LINK_NOT_READY:
|
if cex.type != RNS.Channel.CEType.ME_LINK_NOT_READY:
|
||||||
raise
|
raise
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
try:
|
||||||
|
link_rtt = self._channel._outlet.link.rtt
|
||||||
|
timeout = time.time() + (link_rtt * len(self._channel._tx_ring) * 1)
|
||||||
|
except Exception as e:
|
||||||
|
timeout = time.time() + 15
|
||||||
|
|
||||||
|
while time.time() < timeout and not self._channel.is_ready_to_send():
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
self._eof = True
|
self._eof = True
|
||||||
self.write(bytes())
|
self.write(bytes())
|
||||||
|
|
||||||
|
|||||||
+70
-30
@@ -176,6 +176,9 @@ class Envelope:
|
|||||||
raise ChannelException(CEType.ME_NOT_REGISTERED, f"Unable to find constructor for Channel MSGTYPE {hex(msgtype)}")
|
raise ChannelException(CEType.ME_NOT_REGISTERED, f"Unable to find constructor for Channel MSGTYPE {hex(msgtype)}")
|
||||||
message = ctor()
|
message = ctor()
|
||||||
message.unpack(raw)
|
message.unpack(raw)
|
||||||
|
self.unpacked = True
|
||||||
|
self.message = message
|
||||||
|
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def pack(self) -> bytes:
|
def pack(self) -> bytes:
|
||||||
@@ -183,6 +186,7 @@ class Envelope:
|
|||||||
raise ChannelException(CEType.ME_NO_MSG_TYPE, f"{self.message.__class__} lacks MSGTYPE")
|
raise ChannelException(CEType.ME_NO_MSG_TYPE, f"{self.message.__class__} lacks MSGTYPE")
|
||||||
data = self.message.pack()
|
data = self.message.pack()
|
||||||
self.raw = struct.pack(">HHH", self.message.MSGTYPE, self.sequence, len(data)) + data
|
self.raw = struct.pack(">HHH", self.message.MSGTYPE, self.sequence, len(data)) + data
|
||||||
|
self.packed = True
|
||||||
return self.raw
|
return self.raw
|
||||||
|
|
||||||
def __init__(self, outlet: ChannelOutletBase, message: MessageBase = None, raw: bytes = None, sequence: int = None):
|
def __init__(self, outlet: ChannelOutletBase, message: MessageBase = None, raw: bytes = None, sequence: int = None):
|
||||||
@@ -194,6 +198,8 @@ class Envelope:
|
|||||||
self.sequence = sequence
|
self.sequence = sequence
|
||||||
self.outlet = outlet
|
self.outlet = outlet
|
||||||
self.tries = 0
|
self.tries = 0
|
||||||
|
self.unpacked = False
|
||||||
|
self.packed = False
|
||||||
self.tracked = False
|
self.tracked = False
|
||||||
|
|
||||||
|
|
||||||
@@ -228,13 +234,16 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
WINDOW = 2
|
WINDOW = 2
|
||||||
|
|
||||||
# Absolute minimum window size
|
# Absolute minimum window size
|
||||||
WINDOW_MIN = 1
|
WINDOW_MIN = 2
|
||||||
|
WINDOW_MIN_LIMIT_SLOW = 2
|
||||||
|
WINDOW_MIN_LIMIT_MEDIUM = 5
|
||||||
|
WINDOW_MIN_LIMIT_FAST = 16
|
||||||
|
|
||||||
# The maximum window size for transfers on slow links
|
# The maximum window size for transfers on slow links
|
||||||
WINDOW_MAX_SLOW = 5
|
WINDOW_MAX_SLOW = 5
|
||||||
|
|
||||||
# The maximum window size for transfers on mid-speed links
|
# The maximum window size for transfers on mid-speed links
|
||||||
WINDOW_MAX_MEDIUM = 16
|
WINDOW_MAX_MEDIUM = 12
|
||||||
|
|
||||||
# The maximum window size for transfers on fast links
|
# The maximum window size for transfers on fast links
|
||||||
WINDOW_MAX_FAST = 48
|
WINDOW_MAX_FAST = 48
|
||||||
@@ -249,7 +258,7 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
|
|
||||||
# If the RTT rate is higher than this value,
|
# If the RTT rate is higher than this value,
|
||||||
# the max window size for fast links will be used.
|
# the max window size for fast links will be used.
|
||||||
RTT_FAST = 0.25
|
RTT_FAST = 0.18
|
||||||
RTT_MEDIUM = 0.75
|
RTT_MEDIUM = 0.75
|
||||||
RTT_SLOW = 1.45
|
RTT_SLOW = 1.45
|
||||||
|
|
||||||
@@ -371,22 +380,28 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
|
def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
for existing in ring:
|
for existing in ring:
|
||||||
if existing.sequence > envelope.sequence \
|
|
||||||
and not existing.sequence // 2 > envelope.sequence: # account for overflow
|
if envelope.sequence == existing.sequence:
|
||||||
ring.insert(i, envelope)
|
|
||||||
return True
|
|
||||||
if existing.sequence == envelope.sequence:
|
|
||||||
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence "+str(envelope.sequence), RNS.LOG_EXTREME)
|
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence "+str(envelope.sequence), RNS.LOG_EXTREME)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2):
|
||||||
|
ring.insert(i, envelope)
|
||||||
|
|
||||||
|
envelope.tracked = True
|
||||||
|
return True
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
envelope.tracked = True
|
envelope.tracked = True
|
||||||
ring.append(envelope)
|
ring.append(envelope)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _run_callbacks(self, message: MessageBase):
|
def _run_callbacks(self, message: MessageBase):
|
||||||
with self._lock:
|
cbs = self._message_callbacks.copy()
|
||||||
cbs = self._message_callbacks.copy()
|
|
||||||
|
|
||||||
for cb in cbs:
|
for cb in cbs:
|
||||||
try:
|
try:
|
||||||
@@ -405,12 +420,11 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS
|
window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS
|
||||||
if window_overflow < self._next_rx_sequence:
|
if window_overflow < self._next_rx_sequence:
|
||||||
if envelope.sequence > window_overflow:
|
if envelope.sequence > window_overflow:
|
||||||
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_DEBUG)
|
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_EXTREME)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if envelope.sequence < self._next_rx_sequence:
|
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_EXTREME)
|
||||||
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_DEBUG)
|
return
|
||||||
return
|
|
||||||
|
|
||||||
is_new = self._emplace_envelope(envelope, self._rx_ring)
|
is_new = self._emplace_envelope(envelope, self._rx_ring)
|
||||||
|
|
||||||
@@ -424,11 +438,20 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
if e.sequence == self._next_rx_sequence:
|
if e.sequence == self._next_rx_sequence:
|
||||||
contigous.append(e)
|
contigous.append(e)
|
||||||
self._next_rx_sequence = (self._next_rx_sequence + 1) % Channel.SEQ_MODULUS
|
self._next_rx_sequence = (self._next_rx_sequence + 1) % Channel.SEQ_MODULUS
|
||||||
|
if self._next_rx_sequence == 0:
|
||||||
|
for e in self._rx_ring:
|
||||||
|
if e.sequence == self._next_rx_sequence:
|
||||||
|
contigous.append(e)
|
||||||
|
self._next_rx_sequence = (self._next_rx_sequence + 1) % Channel.SEQ_MODULUS
|
||||||
|
|
||||||
for e in contigous:
|
for e in contigous:
|
||||||
m = e.unpack(self._message_factories)
|
if not e.unpacked:
|
||||||
|
m = e.unpack(self._message_factories)
|
||||||
|
else:
|
||||||
|
m = e.message
|
||||||
|
|
||||||
self._rx_ring.remove(e)
|
self._rx_ring.remove(e)
|
||||||
threading.Thread(target=self._run_callbacks, name="Message Callback", args=[m], daemon=True).start()
|
self._run_callbacks(m)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("An error ocurred while receiving data on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("An error ocurred while receiving data on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
@@ -458,6 +481,7 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
envelope = next(filter(lambda e: self._outlet.get_packet_id(e.packet) == self._outlet.get_packet_id(packet),
|
envelope = next(filter(lambda e: self._outlet.get_packet_id(e.packet) == self._outlet.get_packet_id(packet),
|
||||||
self._tx_ring), None)
|
self._tx_ring), None)
|
||||||
|
|
||||||
if envelope and op(envelope):
|
if envelope and op(envelope):
|
||||||
envelope.tracked = False
|
envelope.tracked = False
|
||||||
if envelope in self._tx_ring:
|
if envelope in self._tx_ring:
|
||||||
@@ -465,11 +489,9 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
|
|
||||||
if self.window < self.window_max:
|
if self.window < self.window_max:
|
||||||
self.window += 1
|
self.window += 1
|
||||||
if (self.window - self.window_min) > (self.window_flexibility-1):
|
|
||||||
self.window_min += 1
|
|
||||||
|
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
RNS.log("Increased "+str(self)+" window to "+str(self.window), RNS.LOG_DEBUG)
|
# RNS.log("Increased "+str(self)+" window to "+str(self.window), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
if self._outlet.rtt != 0:
|
if self._outlet.rtt != 0:
|
||||||
if self._outlet.rtt > Channel.RTT_FAST:
|
if self._outlet.rtt > Channel.RTT_FAST:
|
||||||
@@ -482,28 +504,39 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
self.medium_rate_rounds += 1
|
self.medium_rate_rounds += 1
|
||||||
if self.window_max < Channel.WINDOW_MAX_MEDIUM and self.medium_rate_rounds == Channel.FAST_RATE_THRESHOLD:
|
if self.window_max < Channel.WINDOW_MAX_MEDIUM and self.medium_rate_rounds == Channel.FAST_RATE_THRESHOLD:
|
||||||
self.window_max = Channel.WINDOW_MAX_MEDIUM
|
self.window_max = Channel.WINDOW_MAX_MEDIUM
|
||||||
|
self.window_min = Channel.WINDOW_MIN_LIMIT_MEDIUM
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_EXTREME)
|
# RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG)
|
||||||
|
# RNS.log("Increased "+str(self)+" min window to "+str(self.window_min), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.fast_rate_rounds += 1
|
self.fast_rate_rounds += 1
|
||||||
if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD:
|
if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD:
|
||||||
self.window_max = Channel.WINDOW_MAX_FAST
|
self.window_max = Channel.WINDOW_MAX_FAST
|
||||||
|
self.window_min = Channel.WINDOW_MIN_LIMIT_FAST
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_EXTREME)
|
# RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG)
|
||||||
|
# RNS.log("Increased "+str(self)+" min window to "+str(self.window_min), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Envelope not found in TX ring for "+str(self), RNS.LOG_DEBUG)
|
RNS.log("Envelope not found in TX ring for "+str(self), RNS.LOG_EXTREME)
|
||||||
if not envelope:
|
if not envelope:
|
||||||
RNS.log("Spurious message received on "+str(self), RNS.LOG_EXTREME)
|
RNS.log("Spurious message received on "+str(self), RNS.LOG_EXTREME)
|
||||||
|
|
||||||
def _packet_delivered(self, packet: TPacket):
|
def _packet_delivered(self, packet: TPacket):
|
||||||
self._packet_tx_op(packet, lambda env: True)
|
self._packet_tx_op(packet, lambda env: True)
|
||||||
|
|
||||||
|
def _update_packet_timeouts(self):
|
||||||
|
for envelope in self._tx_ring:
|
||||||
|
updated_timeout = self._get_packet_timeout_time(envelope.tries)
|
||||||
|
if envelope.packet and hasattr(envelope.packet, "receipt") and envelope.packet.receipt and envelope.packet.receipt.timeout:
|
||||||
|
if updated_timeout > envelope.packet.receipt.timeout:
|
||||||
|
envelope.packet.receipt.set_timeout(updated_timeout)
|
||||||
|
|
||||||
def _get_packet_timeout_time(self, tries: int) -> float:
|
def _get_packet_timeout_time(self, tries: int) -> float:
|
||||||
return pow(2, tries - 1) * max(self._outlet.rtt, 0.01) * 5
|
to = pow(1.5, tries - 1) * max(self._outlet.rtt*2.5, 0.025) * (len(self._tx_ring)+1.5)
|
||||||
|
return to
|
||||||
|
|
||||||
def _packet_timeout(self, packet: TPacket):
|
def _packet_timeout(self, packet: TPacket):
|
||||||
def retry_envelope(envelope: Envelope) -> bool:
|
def retry_envelope(envelope: Envelope) -> bool:
|
||||||
@@ -512,20 +545,25 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
self._shutdown() # start on separate thread?
|
self._shutdown() # start on separate thread?
|
||||||
self._outlet.timed_out()
|
self._outlet.timed_out()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
envelope.tries += 1
|
envelope.tries += 1
|
||||||
self._outlet.resend(envelope.packet)
|
self._outlet.resend(envelope.packet)
|
||||||
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)
|
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)
|
||||||
self._outlet.set_packet_timeout_callback(envelope.packet, self._packet_timeout, self._get_packet_timeout_time(envelope.tries))
|
self._outlet.set_packet_timeout_callback(envelope.packet, self._packet_timeout, self._get_packet_timeout_time(envelope.tries))
|
||||||
|
self._update_packet_timeouts()
|
||||||
|
|
||||||
if self.window > self.window_min:
|
if self.window > self.window_min:
|
||||||
self.window -= 1
|
self.window -= 1
|
||||||
if self.window_max > self.window_min:
|
# TODO: Remove at some point
|
||||||
|
# RNS.log("Decreased "+str(self)+" window to "+str(self.window), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
if self.window_max > (self.window_min+self.window_flexibility):
|
||||||
self.window_max -= 1
|
self.window_max -= 1
|
||||||
if (self.window_max - self.window) > (self.window_flexibility-1):
|
# TODO: Remove at some point
|
||||||
self.window_max -= 1
|
# RNS.log("Decreased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
RNS.log("Decreased "+str(self)+" window to "+str(self.window), RNS.LOG_EXTREME)
|
# RNS.log("Decreased "+str(self)+" window to "+str(self.window), RNS.LOG_EXTREME)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -543,20 +581,23 @@ class Channel(contextlib.AbstractContextManager):
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
if not self.is_ready_to_send():
|
if not self.is_ready_to_send():
|
||||||
raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
|
raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
|
||||||
|
|
||||||
envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
|
envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
|
||||||
self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS
|
self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS
|
||||||
|
|
||||||
self._emplace_envelope(envelope, self._tx_ring)
|
self._emplace_envelope(envelope, self._tx_ring)
|
||||||
|
|
||||||
if envelope is None:
|
if envelope is None:
|
||||||
raise BlockingIOError()
|
raise BlockingIOError()
|
||||||
|
|
||||||
envelope.pack()
|
envelope.pack()
|
||||||
if len(envelope.raw) > self._outlet.mdu:
|
if len(envelope.raw) > self._outlet.mdu:
|
||||||
raise ChannelException(CEType.ME_TOO_BIG, f"Packed message too big for packet: {len(envelope.raw)} > {self._outlet.mdu}")
|
raise ChannelException(CEType.ME_TOO_BIG, f"Packed message too big for packet: {len(envelope.raw)} > {self._outlet.mdu}")
|
||||||
|
|
||||||
envelope.packet = self._outlet.send(envelope.raw)
|
envelope.packet = self._outlet.send(envelope.raw)
|
||||||
envelope.tries += 1
|
envelope.tries += 1
|
||||||
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)
|
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)
|
||||||
self._outlet.set_packet_timeout_callback(envelope.packet, self._packet_timeout, self._get_packet_timeout_time(envelope.tries))
|
self._outlet.set_packet_timeout_callback(envelope.packet, self._packet_timeout, self._get_packet_timeout_time(envelope.tries))
|
||||||
|
self._update_packet_timeouts()
|
||||||
|
|
||||||
return envelope
|
return envelope
|
||||||
|
|
||||||
@@ -591,7 +632,6 @@ class LinkChannelOutlet(ChannelOutletBase):
|
|||||||
return packet
|
return packet
|
||||||
|
|
||||||
def resend(self, packet: RNS.Packet) -> RNS.Packet:
|
def resend(self, packet: RNS.Packet) -> RNS.Packet:
|
||||||
RNS.log("Resending packet " + RNS.prettyhexrep(packet.packet_hash), RNS.LOG_DEBUG)
|
|
||||||
receipt = packet.resend()
|
receipt = packet.resend()
|
||||||
if not receipt:
|
if not receipt:
|
||||||
RNS.log("Failed to resend packet", RNS.LOG_ERROR)
|
RNS.log("Failed to resend packet", RNS.LOG_ERROR)
|
||||||
|
|||||||
+10
-2
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -99,7 +99,12 @@ class Destination:
|
|||||||
name_hash = RNS.Identity.full_hash(Destination.expand_name(None, app_name, *aspects).encode("utf-8"))[:(RNS.Identity.NAME_HASH_LENGTH//8)]
|
name_hash = RNS.Identity.full_hash(Destination.expand_name(None, app_name, *aspects).encode("utf-8"))[:(RNS.Identity.NAME_HASH_LENGTH//8)]
|
||||||
addr_hash_material = name_hash
|
addr_hash_material = name_hash
|
||||||
if identity != None:
|
if identity != None:
|
||||||
addr_hash_material += identity.hash
|
if isinstance(identity, RNS.Identity):
|
||||||
|
addr_hash_material += identity.hash
|
||||||
|
elif isinstance(identity, bytes) and len(identity) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
|
||||||
|
addr_hash_material += identity
|
||||||
|
else:
|
||||||
|
raise TypeError("Invalid material supplied for destination hash calculation")
|
||||||
|
|
||||||
return RNS.Identity.full_hash(addr_hash_material)[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
return RNS.Identity.full_hash(addr_hash_material)[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
||||||
|
|
||||||
@@ -177,6 +182,9 @@ class Destination:
|
|||||||
if self.type != Destination.SINGLE:
|
if self.type != Destination.SINGLE:
|
||||||
raise TypeError("Only SINGLE destination types can be announced")
|
raise TypeError("Only SINGLE destination types can be announced")
|
||||||
|
|
||||||
|
if self.direction != Destination.IN:
|
||||||
|
raise TypeError("Only IN destination types can be announced")
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
stale_responses = []
|
stale_responses = []
|
||||||
for entry_tag in self.path_responses:
|
for entry_tag in self.path_responses:
|
||||||
|
|||||||
+7
-3
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -217,7 +217,7 @@ class Identity:
|
|||||||
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
|
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_announce(packet):
|
def validate_announce(packet, only_validate_signature=False):
|
||||||
try:
|
try:
|
||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
destination_hash = packet.destination_hash
|
destination_hash = packet.destination_hash
|
||||||
@@ -238,6 +238,10 @@ class Identity:
|
|||||||
announced_identity.load_public_key(public_key)
|
announced_identity.load_public_key(public_key)
|
||||||
|
|
||||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||||
|
if only_validate_signature:
|
||||||
|
del announced_identity
|
||||||
|
return True
|
||||||
|
|
||||||
hash_material = name_hash+announced_identity.hash
|
hash_material = name_hash+announced_identity.hash
|
||||||
expected_hash = RNS.Identity.full_hash(hash_material)[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
expected_hash = RNS.Identity.full_hash(hash_material)[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
||||||
|
|
||||||
@@ -452,7 +456,7 @@ class Identity:
|
|||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
|
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e))
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
def get_salt(self):
|
def get_salt(self):
|
||||||
return self.hash
|
return self.hash
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ class AX25KISSInterface(Interface):
|
|||||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
@@ -367,5 +366,8 @@ class AX25KISSInterface(Interface):
|
|||||||
|
|
||||||
RNS.log("Reconnected serial port for "+str(self))
|
RNS.log("Reconnected serial port for "+str(self))
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "AX25KISSInterface["+self.name+"]"
|
return "AX25KISSInterface["+self.name+"]"
|
||||||
@@ -82,8 +82,7 @@ class KISSInterface(Interface):
|
|||||||
else:
|
else:
|
||||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
@@ -403,5 +402,8 @@ class KISSInterface(Interface):
|
|||||||
|
|
||||||
RNS.log("Reconnected serial port for "+str(self))
|
RNS.log("Reconnected serial port for "+str(self))
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "KISSInterface["+self.name+"]"
|
return "KISSInterface["+self.name+"]"
|
||||||
@@ -43,21 +43,22 @@ class KISS():
|
|||||||
CMD_CR = 0x05
|
CMD_CR = 0x05
|
||||||
CMD_RADIO_STATE = 0x06
|
CMD_RADIO_STATE = 0x06
|
||||||
CMD_RADIO_LOCK = 0x07
|
CMD_RADIO_LOCK = 0x07
|
||||||
|
CMD_ST_ALOCK = 0x0B
|
||||||
|
CMD_LT_ALOCK = 0x0C
|
||||||
CMD_DETECT = 0x08
|
CMD_DETECT = 0x08
|
||||||
CMD_IMPLICIT = 0x09
|
|
||||||
CMD_LEAVE = 0x0A
|
CMD_LEAVE = 0x0A
|
||||||
CMD_READY = 0x0F
|
CMD_READY = 0x0F
|
||||||
CMD_STAT_RX = 0x21
|
CMD_STAT_RX = 0x21
|
||||||
CMD_STAT_TX = 0x22
|
CMD_STAT_TX = 0x22
|
||||||
CMD_STAT_RSSI = 0x23
|
CMD_STAT_RSSI = 0x23
|
||||||
CMD_STAT_SNR = 0x24
|
CMD_STAT_SNR = 0x24
|
||||||
|
CMD_STAT_CHTM = 0x25
|
||||||
|
CMD_STAT_PHYPRM = 0x26
|
||||||
CMD_BLINK = 0x30
|
CMD_BLINK = 0x30
|
||||||
CMD_RANDOM = 0x40
|
CMD_RANDOM = 0x40
|
||||||
CMD_FB_EXT = 0x41
|
CMD_FB_EXT = 0x41
|
||||||
CMD_FB_READ = 0x42
|
CMD_FB_READ = 0x42
|
||||||
CMD_FB_WRITE = 0x43
|
CMD_FB_WRITE = 0x43
|
||||||
CMD_FB_READL = 0x44
|
|
||||||
CMD_BT_CTRL = 0x46
|
|
||||||
CMD_PLATFORM = 0x48
|
CMD_PLATFORM = 0x48
|
||||||
CMD_MCU = 0x49
|
CMD_MCU = 0x49
|
||||||
CMD_FW_VERSION = 0x50
|
CMD_FW_VERSION = 0x50
|
||||||
@@ -165,8 +166,8 @@ class AndroidBluetoothManager():
|
|||||||
raise IOError("The Bluetooth RFcomm socket could not be connected: "+str(e))
|
raise IOError("The Bluetooth RFcomm socket could not be connected: "+str(e))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_ERROR)
|
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_DEBUG)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self.connected:
|
if self.connected:
|
||||||
@@ -315,7 +316,7 @@ class RNodeInterface(Interface):
|
|||||||
self, owner, name, port, frequency = None, bandwidth = None, txpower = None,
|
self, owner, name, port, frequency = None, bandwidth = None, txpower = None,
|
||||||
sf = None, cr = None, flow_control = False, id_interval = None,
|
sf = None, cr = None, flow_control = False, id_interval = None,
|
||||||
allow_bluetooth = False, target_device_name = None,
|
allow_bluetooth = False, target_device_name = None,
|
||||||
target_device_address = None, id_callsign = None):
|
target_device_address = None, id_callsign = None, st_alock = None, lt_alock = None):
|
||||||
import importlib
|
import importlib
|
||||||
if RNS.vendor.platformutils.is_android():
|
if RNS.vendor.platformutils.is_android():
|
||||||
self.on_android = True
|
self.on_android = True
|
||||||
@@ -348,8 +349,7 @@ class RNodeInterface(Interface):
|
|||||||
else:
|
else:
|
||||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 508
|
self.HW_MTU = 508
|
||||||
|
|
||||||
@@ -373,6 +373,8 @@ class RNodeInterface(Interface):
|
|||||||
self.cr = cr
|
self.cr = cr
|
||||||
self.state = KISS.RADIO_STATE_OFF
|
self.state = KISS.RADIO_STATE_OFF
|
||||||
self.bitrate = 0
|
self.bitrate = 0
|
||||||
|
self.st_alock = st_alock
|
||||||
|
self.lt_alock = lt_alock
|
||||||
self.platform = None
|
self.platform = None
|
||||||
self.display = None
|
self.display = None
|
||||||
self.mcu = None
|
self.mcu = None
|
||||||
@@ -395,7 +397,18 @@ class RNodeInterface(Interface):
|
|||||||
self.r_stat_rx = None
|
self.r_stat_rx = None
|
||||||
self.r_stat_tx = None
|
self.r_stat_tx = None
|
||||||
self.r_stat_rssi = None
|
self.r_stat_rssi = None
|
||||||
|
self.r_stat_snr = None
|
||||||
|
self.r_st_alock = None
|
||||||
|
self.r_lt_alock = None
|
||||||
self.r_random = None
|
self.r_random = None
|
||||||
|
self.r_airtime_short = 0.0
|
||||||
|
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.packet_queue = []
|
self.packet_queue = []
|
||||||
self.flow_control = flow_control
|
self.flow_control = flow_control
|
||||||
@@ -426,6 +439,14 @@ class RNodeInterface(Interface):
|
|||||||
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
|
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
|
||||||
self.validcfg = False
|
self.validcfg = False
|
||||||
|
|
||||||
|
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
|
||||||
|
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
|
||||||
|
self.validcfg = False
|
||||||
|
|
||||||
|
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
|
||||||
|
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
|
||||||
|
self.validcfg = False
|
||||||
|
|
||||||
if id_interval != None and id_callsign != None:
|
if id_interval != None and id_callsign != None:
|
||||||
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
|
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
|
||||||
self.should_id = True
|
self.should_id = True
|
||||||
@@ -616,6 +637,12 @@ class RNodeInterface(Interface):
|
|||||||
self.setCodingRate()
|
self.setCodingRate()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
|
self.setSTALock()
|
||||||
|
time.sleep(0.15)
|
||||||
|
|
||||||
|
self.setLTALock()
|
||||||
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setRadioState(KISS.RADIO_STATE_ON)
|
self.setRadioState(KISS.RADIO_STATE_ON)
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
@@ -740,6 +767,30 @@ class RNodeInterface(Interface):
|
|||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
|
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
|
||||||
|
|
||||||
|
def setSTALock(self):
|
||||||
|
if self.st_alock != None:
|
||||||
|
at = int(self.st_alock*100)
|
||||||
|
c1 = at >> 8 & 0xFF
|
||||||
|
c2 = at & 0xFF
|
||||||
|
data = KISS.escape(bytes([c1])+bytes([c2]))
|
||||||
|
|
||||||
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
|
||||||
|
written = self.write_mux(kiss_command)
|
||||||
|
if written != len(kiss_command):
|
||||||
|
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
|
||||||
|
|
||||||
|
def setLTALock(self):
|
||||||
|
if self.lt_alock != None:
|
||||||
|
at = int(self.lt_alock*100)
|
||||||
|
c1 = at >> 8 & 0xFF
|
||||||
|
c2 = at & 0xFF
|
||||||
|
data = KISS.escape(bytes([c1])+bytes([c2]))
|
||||||
|
|
||||||
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
|
||||||
|
written = self.write_mux(kiss_command)
|
||||||
|
if written != len(kiss_command):
|
||||||
|
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
|
||||||
|
|
||||||
def setRadioState(self, state):
|
def setRadioState(self, state):
|
||||||
self.state = state
|
self.state = state
|
||||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
|
||||||
@@ -993,6 +1044,84 @@ class RNodeInterface(Interface):
|
|||||||
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
||||||
elif (command == KISS.CMD_STAT_SNR):
|
elif (command == KISS.CMD_STAT_SNR):
|
||||||
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
|
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
|
||||||
|
elif (command == KISS.CMD_ST_ALOCK):
|
||||||
|
if (byte == KISS.FESC):
|
||||||
|
escape = True
|
||||||
|
else:
|
||||||
|
if (escape):
|
||||||
|
if (byte == KISS.TFEND):
|
||||||
|
byte = KISS.FEND
|
||||||
|
if (byte == KISS.TFESC):
|
||||||
|
byte = KISS.FESC
|
||||||
|
escape = False
|
||||||
|
command_buffer = command_buffer+bytes([byte])
|
||||||
|
if (len(command_buffer) == 2):
|
||||||
|
at = command_buffer[0] << 8 | command_buffer[1]
|
||||||
|
self.r_st_alock = at/100.0
|
||||||
|
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
|
||||||
|
elif (command == KISS.CMD_LT_ALOCK):
|
||||||
|
if (byte == KISS.FESC):
|
||||||
|
escape = True
|
||||||
|
else:
|
||||||
|
if (escape):
|
||||||
|
if (byte == KISS.TFEND):
|
||||||
|
byte = KISS.FEND
|
||||||
|
if (byte == KISS.TFESC):
|
||||||
|
byte = KISS.FESC
|
||||||
|
escape = False
|
||||||
|
command_buffer = command_buffer+bytes([byte])
|
||||||
|
if (len(command_buffer) == 2):
|
||||||
|
at = command_buffer[0] << 8 | command_buffer[1]
|
||||||
|
self.r_lt_alock = at/100.0
|
||||||
|
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
|
||||||
|
elif (command == KISS.CMD_STAT_CHTM):
|
||||||
|
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) == 8):
|
||||||
|
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]
|
||||||
|
|
||||||
|
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
|
||||||
|
elif (command == KISS.CMD_STAT_PHYPRM):
|
||||||
|
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) == 10):
|
||||||
|
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]
|
||||||
|
|
||||||
|
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:
|
||||||
|
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)
|
||||||
elif (command == KISS.CMD_RANDOM):
|
elif (command == KISS.CMD_RANDOM):
|
||||||
self.r_random = byte
|
self.r_random = byte
|
||||||
elif (command == KISS.CMD_PLATFORM):
|
elif (command == KISS.CMD_PLATFORM):
|
||||||
@@ -1104,6 +1233,9 @@ class RNodeInterface(Interface):
|
|||||||
self.setRadioState(KISS.RADIO_STATE_OFF)
|
self.setRadioState(KISS.RADIO_STATE_OFF)
|
||||||
self.leave()
|
self.leave()
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "RNodeInterface["+str(self.name)+"]"
|
return "RNodeInterface["+str(self.name)+"]"
|
||||||
|
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ class SerialInterface(Interface):
|
|||||||
else:
|
else:
|
||||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
@@ -254,5 +253,8 @@ class SerialInterface(Interface):
|
|||||||
|
|
||||||
RNS.log("Reconnected serial port for "+str(self))
|
RNS.log("Reconnected serial port for "+str(self))
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SerialInterface["+self.name+"]"
|
return "SerialInterface["+self.name+"]"
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ class AutoInterface(Interface):
|
|||||||
|
|
||||||
BITRATE_GUESS = 10*1000*1000
|
BITRATE_GUESS = 10*1000*1000
|
||||||
|
|
||||||
MULTI_IF_DEQUE_LEN = 64
|
MULTI_IF_DEQUE_LEN = 48
|
||||||
|
MULTI_IF_DEQUE_TTL = 0.75
|
||||||
|
|
||||||
def handler_factory(self, callback):
|
def handler_factory(self, callback):
|
||||||
def create_handler(*args, **keys):
|
def create_handler(*args, **keys):
|
||||||
@@ -75,11 +76,9 @@ class AutoInterface(Interface):
|
|||||||
|
|
||||||
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
|
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
|
||||||
from RNS.vendor.ifaddr import niwrapper
|
from RNS.vendor.ifaddr import niwrapper
|
||||||
|
super().__init__()
|
||||||
self.netinfo = niwrapper
|
self.netinfo = niwrapper
|
||||||
|
|
||||||
self.rxb = 0
|
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
self.IN = True
|
self.IN = True
|
||||||
@@ -93,6 +92,7 @@ class AutoInterface(Interface):
|
|||||||
self.multicast_echoes = {}
|
self.multicast_echoes = {}
|
||||||
self.timed_out_interfaces = {}
|
self.timed_out_interfaces = {}
|
||||||
self.mif_deque = deque(maxlen=AutoInterface.MULTI_IF_DEQUE_LEN)
|
self.mif_deque = deque(maxlen=AutoInterface.MULTI_IF_DEQUE_LEN)
|
||||||
|
self.mif_deque_times = deque(maxlen=AutoInterface.MULTI_IF_DEQUE_LEN)
|
||||||
self.carrier_changed = False
|
self.carrier_changed = False
|
||||||
|
|
||||||
self.outbound_udp_socket = None
|
self.outbound_udp_socket = None
|
||||||
@@ -103,6 +103,11 @@ class AutoInterface(Interface):
|
|||||||
self.peering_timeout = AutoInterface.PEERING_TIMEOUT
|
self.peering_timeout = AutoInterface.PEERING_TIMEOUT
|
||||||
self.multicast_echo_timeout = AutoInterface.PEERING_TIMEOUT/2
|
self.multicast_echo_timeout = AutoInterface.PEERING_TIMEOUT/2
|
||||||
|
|
||||||
|
# Increase peering timeout on Android, due to potential
|
||||||
|
# low-power modes implemented on many chipsets.
|
||||||
|
if RNS.vendor.platformutils.is_android():
|
||||||
|
self.peering_timeout *= 3
|
||||||
|
|
||||||
if allowed_interfaces == None:
|
if allowed_interfaces == None:
|
||||||
self.allowed_interfaces = []
|
self.allowed_interfaces = []
|
||||||
else:
|
else:
|
||||||
@@ -396,8 +401,16 @@ class AutoInterface(Interface):
|
|||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
data_hash = RNS.Identity.full_hash(data)
|
data_hash = RNS.Identity.full_hash(data)
|
||||||
if not data_hash in self.mif_deque:
|
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.append(data_hash)
|
||||||
|
self.mif_deque_times.append([data_hash, time.time()])
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
@@ -418,6 +431,11 @@ class AutoInterface(Interface):
|
|||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
|
|
||||||
|
|
||||||
|
# Until per-device sub-interfacing is implemented,
|
||||||
|
# ingress limiting should be disabled on AutoInterface
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "AutoInterface["+self.name+"]"
|
return "AutoInterface["+self.name+"]"
|
||||||
|
|
||||||
|
|||||||
@@ -390,8 +390,7 @@ class I2PInterfacePeer(Interface):
|
|||||||
TUNNEL_STATE_STALE = 0x02
|
TUNNEL_STATE_STALE = 0x02
|
||||||
|
|
||||||
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
|
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
@@ -832,8 +831,7 @@ class I2PInterface(Interface):
|
|||||||
BITRATE_GUESS = 256*1000
|
BITRATE_GUESS = 256*1000
|
||||||
|
|
||||||
def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
|
def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
@@ -964,6 +962,12 @@ class I2PInterface(Interface):
|
|||||||
def processOutgoing(self, data):
|
def processOutgoing(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def received_announce(self, from_spawned=False):
|
||||||
|
if from_spawned: self.ia_freq_deque.append(time.time())
|
||||||
|
|
||||||
|
def sent_announce(self, from_spawned=False):
|
||||||
|
if from_spawned: self.oa_freq_deque.append(time.time())
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
|
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
|
||||||
self.i2p.stop()
|
self.i2p.stop()
|
||||||
|
|||||||
+147
-3
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
import RNS
|
import RNS
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
class Interface:
|
class Interface:
|
||||||
IN = False
|
IN = False
|
||||||
@@ -39,18 +40,160 @@ class Interface:
|
|||||||
MODE_BOUNDARY = 0x05
|
MODE_BOUNDARY = 0x05
|
||||||
MODE_GATEWAY = 0x06
|
MODE_GATEWAY = 0x06
|
||||||
|
|
||||||
# Which interface modes a Transport Node
|
# Which interface modes a Transport Node should
|
||||||
# should actively discover paths for.
|
# actively discover paths for.
|
||||||
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
||||||
|
|
||||||
|
# How many samples to use for announce
|
||||||
|
# frequency calculations
|
||||||
|
IA_FREQ_SAMPLES = 6
|
||||||
|
OA_FREQ_SAMPLES = 6
|
||||||
|
|
||||||
|
# Maximum amount of ingress limited announces
|
||||||
|
# to hold at any given time.
|
||||||
|
MAX_HELD_ANNOUNCES = 256
|
||||||
|
|
||||||
|
# How long a spawned interface will be
|
||||||
|
# considered to be newly created. Two
|
||||||
|
# hours by default.
|
||||||
|
IC_NEW_TIME = 2*60*60
|
||||||
|
IC_BURST_FREQ_NEW = 3.5
|
||||||
|
IC_BURST_FREQ = 12
|
||||||
|
IC_BURST_HOLD = 1*60
|
||||||
|
IC_BURST_PENALTY = 5*60
|
||||||
|
IC_HELD_RELEASE_INTERVAL = 30
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.rxb = 0
|
self.rxb = 0
|
||||||
self.txb = 0
|
self.txb = 0
|
||||||
|
self.created = time.time()
|
||||||
self.online = False
|
self.online = False
|
||||||
|
|
||||||
|
self.ingress_control = True
|
||||||
|
self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES
|
||||||
|
self.ic_burst_hold = Interface.IC_BURST_HOLD
|
||||||
|
self.ic_burst_active = False
|
||||||
|
self.ic_burst_activated = 0
|
||||||
|
self.ic_held_release = 0
|
||||||
|
self.ic_burst_freq_new = Interface.IC_BURST_FREQ_NEW
|
||||||
|
self.ic_burst_freq = Interface.IC_BURST_FREQ
|
||||||
|
self.ic_new_time = Interface.IC_NEW_TIME
|
||||||
|
self.ic_burst_penalty = Interface.IC_BURST_PENALTY
|
||||||
|
self.ic_held_release_interval = Interface.IC_HELD_RELEASE_INTERVAL
|
||||||
|
self.held_announces = {}
|
||||||
|
|
||||||
|
self.ia_freq_deque = deque(maxlen=Interface.IA_FREQ_SAMPLES)
|
||||||
|
self.oa_freq_deque = deque(maxlen=Interface.OA_FREQ_SAMPLES)
|
||||||
|
|
||||||
def get_hash(self):
|
def get_hash(self):
|
||||||
return RNS.Identity.full_hash(str(self).encode("utf-8"))
|
return RNS.Identity.full_hash(str(self).encode("utf-8"))
|
||||||
|
|
||||||
|
# This is a generic function for determining when an interface
|
||||||
|
# should activate ingress limiting. Since this can vary for
|
||||||
|
# different interface types, this function should be overwritten
|
||||||
|
# in case a particular interface requires a different approach.
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
if self.ingress_control:
|
||||||
|
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
|
||||||
|
ia_freq = self.incoming_announce_frequency()
|
||||||
|
|
||||||
|
if self.ic_burst_active:
|
||||||
|
if ia_freq < freq_threshold and time.time() > self.ic_burst_activated+self.ic_burst_hold:
|
||||||
|
self.ic_burst_active = False
|
||||||
|
self.ic_held_release = time.time() + self.ic_burst_penalty
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
if ia_freq > freq_threshold:
|
||||||
|
self.ic_burst_active = True
|
||||||
|
self.ic_burst_activated = time.time()
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def age(self):
|
||||||
|
return time.time()-self.created
|
||||||
|
|
||||||
|
def hold_announce(self, announce_packet):
|
||||||
|
if announce_packet.destination_hash in self.held_announces:
|
||||||
|
self.held_announces[announce_packet.destination_hash] = announce_packet
|
||||||
|
elif not len(self.held_announces) >= self.ic_max_held_announces:
|
||||||
|
self.held_announces[announce_packet.destination_hash] = announce_packet
|
||||||
|
|
||||||
|
def process_held_announces(self):
|
||||||
|
try:
|
||||||
|
if not self.should_ingress_limit() and len(self.held_announces) > 0 and time.time() > self.ic_held_release:
|
||||||
|
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
|
||||||
|
ia_freq = self.incoming_announce_frequency()
|
||||||
|
if ia_freq < freq_threshold:
|
||||||
|
selected_announce_packet = None
|
||||||
|
min_hops = RNS.Transport.PATHFINDER_M
|
||||||
|
for destination_hash in self.held_announces:
|
||||||
|
announce_packet = self.held_announces[destination_hash]
|
||||||
|
if announce_packet.hops < min_hops:
|
||||||
|
min_hops = announce_packet.hops
|
||||||
|
selected_announce_packet = announce_packet
|
||||||
|
|
||||||
|
if selected_announce_packet != None:
|
||||||
|
RNS.log("Releasing held announce packet "+str(selected_announce_packet)+" from "+str(self), RNS.LOG_EXTREME)
|
||||||
|
self.ic_held_release = time.time() + self.ic_held_release_interval
|
||||||
|
self.held_announces.pop(selected_announce_packet.destination_hash)
|
||||||
|
def release():
|
||||||
|
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
|
||||||
|
threading.Thread(target=release, daemon=True).start()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("An error occurred while processing held announces for "+str(self), RNS.LOG_ERROR)
|
||||||
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
def received_announce(self):
|
||||||
|
self.ia_freq_deque.append(time.time())
|
||||||
|
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||||
|
self.parent_interface.received_announce(from_spawned=True)
|
||||||
|
|
||||||
|
def sent_announce(self):
|
||||||
|
self.oa_freq_deque.append(time.time())
|
||||||
|
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||||
|
self.parent_interface.sent_announce(from_spawned=True)
|
||||||
|
|
||||||
|
def incoming_announce_frequency(self):
|
||||||
|
if not len(self.ia_freq_deque) > 1:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
dq_len = len(self.ia_freq_deque)
|
||||||
|
delta_sum = 0
|
||||||
|
for i in range(1,dq_len):
|
||||||
|
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
|
||||||
|
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
|
||||||
|
|
||||||
|
if delta_sum == 0:
|
||||||
|
avg = 0
|
||||||
|
else:
|
||||||
|
avg = 1/(delta_sum/(dq_len))
|
||||||
|
|
||||||
|
return avg
|
||||||
|
|
||||||
|
def outgoing_announce_frequency(self):
|
||||||
|
if not len(self.oa_freq_deque) > 1:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
dq_len = len(self.oa_freq_deque)
|
||||||
|
delta_sum = 0
|
||||||
|
for i in range(1,dq_len):
|
||||||
|
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
|
||||||
|
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
|
||||||
|
|
||||||
|
if delta_sum == 0:
|
||||||
|
avg = 0
|
||||||
|
else:
|
||||||
|
avg = 1/(delta_sum/(dq_len))
|
||||||
|
|
||||||
|
return avg
|
||||||
|
|
||||||
def process_announce_queue(self):
|
def process_announce_queue(self):
|
||||||
if not hasattr(self, "announce_cap"):
|
if not hasattr(self, "announce_cap"):
|
||||||
self.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
|
self.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
|
||||||
@@ -79,6 +222,7 @@ class Interface:
|
|||||||
self.announce_allowed_at = now + wait_time
|
self.announce_allowed_at = now + wait_time
|
||||||
|
|
||||||
self.processOutgoing(selected["raw"])
|
self.processOutgoing(selected["raw"])
|
||||||
|
self.sent_announce()
|
||||||
|
|
||||||
if selected in self.announce_queue:
|
if selected in self.announce_queue:
|
||||||
self.announce_queue.remove(selected)
|
self.announce_queue.remove(selected)
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ class KISSInterface(Interface):
|
|||||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
@@ -349,5 +348,8 @@ class KISSInterface(Interface):
|
|||||||
|
|
||||||
RNS.log("Reconnected serial port for "+str(self))
|
RNS.log("Reconnected serial port for "+str(self))
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "KISSInterface["+self.name+"]"
|
return "KISSInterface["+self.name+"]"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -53,8 +53,7 @@ class LocalClientInterface(Interface):
|
|||||||
RECONNECT_WAIT = 3
|
RECONNECT_WAIT = 3
|
||||||
|
|
||||||
def __init__(self, owner, name, target_port = None, connected_socket=None):
|
def __init__(self, owner, name, target_port = None, connected_socket=None):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
# self.rxptime = 0
|
# self.rxptime = 0
|
||||||
@@ -103,6 +102,9 @@ class LocalClientInterface(Interface):
|
|||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.socket.connect((self.target_ip, self.target_port))
|
self.socket.connect((self.target_ip, self.target_port))
|
||||||
@@ -280,8 +282,7 @@ class LocalClientInterface(Interface):
|
|||||||
class LocalServerInterface(Interface):
|
class LocalServerInterface(Interface):
|
||||||
|
|
||||||
def __init__(self, owner, bindport=None):
|
def __init__(self, owner, bindport=None):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
self.online = False
|
self.online = False
|
||||||
self.clients = 0
|
self.clients = 0
|
||||||
|
|
||||||
@@ -329,7 +330,7 @@ class LocalServerInterface(Interface):
|
|||||||
spawned_interface.target_port = str(handler.client_address[1])
|
spawned_interface.target_port = str(handler.client_address[1])
|
||||||
spawned_interface.parent_interface = self
|
spawned_interface.parent_interface = self
|
||||||
spawned_interface.bitrate = self.bitrate
|
spawned_interface.bitrate = self.bitrate
|
||||||
RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_EXTREME)
|
# RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_EXTREME)
|
||||||
RNS.Transport.interfaces.append(spawned_interface)
|
RNS.Transport.interfaces.append(spawned_interface)
|
||||||
RNS.Transport.local_client_interfaces.append(spawned_interface)
|
RNS.Transport.local_client_interfaces.append(spawned_interface)
|
||||||
self.clients += 1
|
self.clients += 1
|
||||||
@@ -338,6 +339,12 @@ class LocalServerInterface(Interface):
|
|||||||
def processOutgoing(self, data):
|
def processOutgoing(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def received_announce(self, from_spawned=False):
|
||||||
|
if from_spawned: self.ia_freq_deque.append(time.time())
|
||||||
|
|
||||||
|
def sent_announce(self, from_spawned=False):
|
||||||
|
if from_spawned: self.oa_freq_deque.append(time.time())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Shared Instance["+str(self.bind_port)+"]"
|
return "Shared Instance["+str(self.bind_port)+"]"
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ class PipeInterface(Interface):
|
|||||||
if respawn_delay == None:
|
if respawn_delay == None:
|
||||||
respawn_delay = 5
|
respawn_delay = 5
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -43,6 +43,8 @@ class KISS():
|
|||||||
CMD_CR = 0x05
|
CMD_CR = 0x05
|
||||||
CMD_RADIO_STATE = 0x06
|
CMD_RADIO_STATE = 0x06
|
||||||
CMD_RADIO_LOCK = 0x07
|
CMD_RADIO_LOCK = 0x07
|
||||||
|
CMD_ST_ALOCK = 0x0B
|
||||||
|
CMD_LT_ALOCK = 0x0C
|
||||||
CMD_DETECT = 0x08
|
CMD_DETECT = 0x08
|
||||||
CMD_LEAVE = 0x0A
|
CMD_LEAVE = 0x0A
|
||||||
CMD_READY = 0x0F
|
CMD_READY = 0x0F
|
||||||
@@ -50,6 +52,8 @@ class KISS():
|
|||||||
CMD_STAT_TX = 0x22
|
CMD_STAT_TX = 0x22
|
||||||
CMD_STAT_RSSI = 0x23
|
CMD_STAT_RSSI = 0x23
|
||||||
CMD_STAT_SNR = 0x24
|
CMD_STAT_SNR = 0x24
|
||||||
|
CMD_STAT_CHTM = 0x25
|
||||||
|
CMD_STAT_PHYPRM = 0x26
|
||||||
CMD_BLINK = 0x30
|
CMD_BLINK = 0x30
|
||||||
CMD_RANDOM = 0x40
|
CMD_RANDOM = 0x40
|
||||||
CMD_FB_EXT = 0x41
|
CMD_FB_EXT = 0x41
|
||||||
@@ -98,7 +102,7 @@ class RNodeInterface(Interface):
|
|||||||
|
|
||||||
RECONNECT_WAIT = 5
|
RECONNECT_WAIT = 5
|
||||||
|
|
||||||
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None):
|
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None, st_alock = None, lt_alock = None):
|
||||||
if RNS.vendor.platformutils.is_android():
|
if RNS.vendor.platformutils.is_android():
|
||||||
raise SystemError("Invlaid interface type. The Android-specific RNode interface must be used on Android")
|
raise SystemError("Invlaid interface type. The Android-specific RNode interface must be used on Android")
|
||||||
|
|
||||||
@@ -110,8 +114,7 @@ class RNodeInterface(Interface):
|
|||||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 508
|
self.HW_MTU = 508
|
||||||
|
|
||||||
@@ -135,6 +138,8 @@ class RNodeInterface(Interface):
|
|||||||
self.cr = cr
|
self.cr = cr
|
||||||
self.state = KISS.RADIO_STATE_OFF
|
self.state = KISS.RADIO_STATE_OFF
|
||||||
self.bitrate = 0
|
self.bitrate = 0
|
||||||
|
self.st_alock = st_alock
|
||||||
|
self.lt_alock = lt_alock
|
||||||
self.platform = None
|
self.platform = None
|
||||||
self.display = None
|
self.display = None
|
||||||
self.mcu = None
|
self.mcu = None
|
||||||
@@ -157,7 +162,18 @@ class RNodeInterface(Interface):
|
|||||||
self.r_stat_rx = None
|
self.r_stat_rx = None
|
||||||
self.r_stat_tx = None
|
self.r_stat_tx = None
|
||||||
self.r_stat_rssi = None
|
self.r_stat_rssi = None
|
||||||
|
self.r_stat_snr = None
|
||||||
|
self.r_st_alock = None
|
||||||
|
self.r_lt_alock = None
|
||||||
self.r_random = None
|
self.r_random = None
|
||||||
|
self.r_airtime_short = 0.0
|
||||||
|
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.packet_queue = []
|
self.packet_queue = []
|
||||||
self.flow_control = flow_control
|
self.flow_control = flow_control
|
||||||
@@ -185,6 +201,14 @@ class RNodeInterface(Interface):
|
|||||||
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
|
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
|
||||||
self.validcfg = False
|
self.validcfg = False
|
||||||
|
|
||||||
|
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
|
||||||
|
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
|
||||||
|
self.validcfg = False
|
||||||
|
|
||||||
|
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
|
||||||
|
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
|
||||||
|
self.validcfg = False
|
||||||
|
|
||||||
if id_interval != None and id_callsign != None:
|
if id_interval != None and id_callsign != None:
|
||||||
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
|
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
|
||||||
self.should_id = True
|
self.should_id = True
|
||||||
@@ -280,6 +304,8 @@ class RNodeInterface(Interface):
|
|||||||
self.setTXPower()
|
self.setTXPower()
|
||||||
self.setSpreadingFactor()
|
self.setSpreadingFactor()
|
||||||
self.setCodingRate()
|
self.setCodingRate()
|
||||||
|
self.setSTALock()
|
||||||
|
self.setLTALock()
|
||||||
self.setRadioState(KISS.RADIO_STATE_ON)
|
self.setRadioState(KISS.RADIO_STATE_ON)
|
||||||
|
|
||||||
def detect(self):
|
def detect(self):
|
||||||
@@ -384,6 +410,30 @@ class RNodeInterface(Interface):
|
|||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
|
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
|
||||||
|
|
||||||
|
def setSTALock(self):
|
||||||
|
if self.st_alock != None:
|
||||||
|
at = int(self.st_alock*100)
|
||||||
|
c1 = at >> 8 & 0xFF
|
||||||
|
c2 = at & 0xFF
|
||||||
|
data = KISS.escape(bytes([c1])+bytes([c2]))
|
||||||
|
|
||||||
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
|
||||||
|
written = self.serial.write(kiss_command)
|
||||||
|
if written != len(kiss_command):
|
||||||
|
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
|
||||||
|
|
||||||
|
def setLTALock(self):
|
||||||
|
if self.lt_alock != None:
|
||||||
|
at = int(self.lt_alock*100)
|
||||||
|
c1 = at >> 8 & 0xFF
|
||||||
|
c2 = at & 0xFF
|
||||||
|
data = KISS.escape(bytes([c1])+bytes([c2]))
|
||||||
|
|
||||||
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
|
||||||
|
written = self.serial.write(kiss_command)
|
||||||
|
if written != len(kiss_command):
|
||||||
|
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
|
||||||
|
|
||||||
def setRadioState(self, state):
|
def setRadioState(self, state):
|
||||||
self.state = state
|
self.state = state
|
||||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
|
||||||
@@ -621,6 +671,84 @@ class RNodeInterface(Interface):
|
|||||||
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
||||||
elif (command == KISS.CMD_STAT_SNR):
|
elif (command == KISS.CMD_STAT_SNR):
|
||||||
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
|
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
|
||||||
|
elif (command == KISS.CMD_ST_ALOCK):
|
||||||
|
if (byte == KISS.FESC):
|
||||||
|
escape = True
|
||||||
|
else:
|
||||||
|
if (escape):
|
||||||
|
if (byte == KISS.TFEND):
|
||||||
|
byte = KISS.FEND
|
||||||
|
if (byte == KISS.TFESC):
|
||||||
|
byte = KISS.FESC
|
||||||
|
escape = False
|
||||||
|
command_buffer = command_buffer+bytes([byte])
|
||||||
|
if (len(command_buffer) == 2):
|
||||||
|
at = command_buffer[0] << 8 | command_buffer[1]
|
||||||
|
self.r_st_alock = at/100.0
|
||||||
|
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
|
||||||
|
elif (command == KISS.CMD_LT_ALOCK):
|
||||||
|
if (byte == KISS.FESC):
|
||||||
|
escape = True
|
||||||
|
else:
|
||||||
|
if (escape):
|
||||||
|
if (byte == KISS.TFEND):
|
||||||
|
byte = KISS.FEND
|
||||||
|
if (byte == KISS.TFESC):
|
||||||
|
byte = KISS.FESC
|
||||||
|
escape = False
|
||||||
|
command_buffer = command_buffer+bytes([byte])
|
||||||
|
if (len(command_buffer) == 2):
|
||||||
|
at = command_buffer[0] << 8 | command_buffer[1]
|
||||||
|
self.r_lt_alock = at/100.0
|
||||||
|
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
|
||||||
|
elif (command == KISS.CMD_STAT_CHTM):
|
||||||
|
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) == 8):
|
||||||
|
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]
|
||||||
|
|
||||||
|
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
|
||||||
|
elif (command == KISS.CMD_STAT_PHYPRM):
|
||||||
|
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) == 10):
|
||||||
|
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]
|
||||||
|
|
||||||
|
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:
|
||||||
|
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)
|
||||||
elif (command == KISS.CMD_RANDOM):
|
elif (command == KISS.CMD_RANDOM):
|
||||||
self.r_random = byte
|
self.r_random = byte
|
||||||
elif (command == KISS.CMD_PLATFORM):
|
elif (command == KISS.CMD_PLATFORM):
|
||||||
@@ -709,5 +837,8 @@ class RNodeInterface(Interface):
|
|||||||
self.setRadioState(KISS.RADIO_STATE_OFF)
|
self.setRadioState(KISS.RADIO_STATE_OFF)
|
||||||
self.leave()
|
self.leave()
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "RNodeInterface["+str(self.name)+"]"
|
return "RNodeInterface["+str(self.name)+"]"
|
||||||
|
|||||||
@@ -60,8 +60,7 @@ class SerialInterface(Interface):
|
|||||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
@@ -201,5 +200,8 @@ class SerialInterface(Interface):
|
|||||||
|
|
||||||
RNS.log("Reconnected serial port for "+str(self))
|
RNS.log("Reconnected serial port for "+str(self))
|
||||||
|
|
||||||
|
def should_ingress_limit(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SerialInterface["+self.name+"]"
|
return "SerialInterface["+self.name+"]"
|
||||||
|
|||||||
@@ -79,8 +79,7 @@ class TCPClientInterface(Interface):
|
|||||||
I2P_PROBES = 5
|
I2P_PROBES = 5
|
||||||
|
|
||||||
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
|
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
@@ -419,8 +418,7 @@ class TCPServerInterface(Interface):
|
|||||||
return ifaddr[netinfo.AF_INET][0]["broadcast"]
|
return ifaddr[netinfo.AF_INET][0]["broadcast"]
|
||||||
|
|
||||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
|
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
@@ -505,6 +503,12 @@ class TCPServerInterface(Interface):
|
|||||||
self.clients += 1
|
self.clients += 1
|
||||||
spawned_interface.read_loop()
|
spawned_interface.read_loop()
|
||||||
|
|
||||||
|
def received_announce(self, from_spawned=False):
|
||||||
|
if from_spawned: self.ia_freq_deque.append(time.time())
|
||||||
|
|
||||||
|
def sent_announce(self, from_spawned=False):
|
||||||
|
if from_spawned: self.oa_freq_deque.append(time.time())
|
||||||
|
|
||||||
def processOutgoing(self, data):
|
def processOutgoing(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ class UDPInterface(Interface):
|
|||||||
return ifaddr[netinfo.AF_INET][0]["broadcast"]
|
return ifaddr[netinfo.AF_INET][0]["broadcast"]
|
||||||
|
|
||||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
|
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
|
||||||
self.rxb = 0
|
super().__init__()
|
||||||
self.txb = 0
|
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
|
|||||||
+63
-47
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -225,15 +225,18 @@ class Link:
|
|||||||
self.hash = self.link_id
|
self.hash = self.link_id
|
||||||
|
|
||||||
def handshake(self):
|
def handshake(self):
|
||||||
self.status = Link.HANDSHAKE
|
if self.status == Link.PENDING and self.prv != None:
|
||||||
self.shared_key = self.prv.exchange(self.peer_pub)
|
self.status = Link.HANDSHAKE
|
||||||
|
self.shared_key = self.prv.exchange(self.peer_pub)
|
||||||
|
|
||||||
self.derived_key = RNS.Cryptography.hkdf(
|
self.derived_key = RNS.Cryptography.hkdf(
|
||||||
length=32,
|
length=32,
|
||||||
derive_from=self.shared_key,
|
derive_from=self.shared_key,
|
||||||
salt=self.get_salt(),
|
salt=self.get_salt(),
|
||||||
context=self.get_context(),
|
context=self.get_context(),
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
RNS.log("Handshake attempt on "+str(self)+" with invalid state "+str(self.status), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|
||||||
def prove(self):
|
def prove(self):
|
||||||
@@ -261,41 +264,50 @@ class Link:
|
|||||||
self.had_outbound()
|
self.had_outbound()
|
||||||
|
|
||||||
def validate_proof(self, packet):
|
def validate_proof(self, packet):
|
||||||
if self.status == Link.PENDING:
|
try:
|
||||||
if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2:
|
if self.status == Link.PENDING:
|
||||||
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2]
|
if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2:
|
||||||
peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE]
|
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2]
|
||||||
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
|
peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE]
|
||||||
self.handshake()
|
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
|
||||||
|
self.handshake()
|
||||||
|
|
||||||
self.establishment_cost += len(packet.raw)
|
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
|
||||||
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
|
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
|
||||||
|
|
||||||
if self.destination.identity.validate(signature, signed_data):
|
if self.destination.identity.validate(signature, signed_data):
|
||||||
self.rtt = time.time() - self.request_time
|
if self.status != Link.HANDSHAKE:
|
||||||
self.attached_interface = packet.receiving_interface
|
raise IOError("Invalid link state for proof validation: "+str(self.status))
|
||||||
self.__remote_identity = self.destination.identity
|
|
||||||
self.status = Link.ACTIVE
|
|
||||||
self.activated_at = time.time()
|
|
||||||
self.last_proof = self.activated_at
|
|
||||||
RNS.Transport.activate_link(self)
|
|
||||||
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE)
|
|
||||||
|
|
||||||
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
|
self.rtt = time.time() - self.request_time
|
||||||
self.establishment_rate = self.establishment_cost/self.rtt
|
self.attached_interface = packet.receiving_interface
|
||||||
|
self.__remote_identity = self.destination.identity
|
||||||
|
self.status = Link.ACTIVE
|
||||||
|
self.activated_at = time.time()
|
||||||
|
self.last_proof = self.activated_at
|
||||||
|
RNS.Transport.activate_link(self)
|
||||||
|
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
rtt_data = umsgpack.packb(self.rtt)
|
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
|
||||||
rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT)
|
self.establishment_rate = self.establishment_cost/self.rtt
|
||||||
rtt_packet.send()
|
|
||||||
self.had_outbound()
|
|
||||||
|
|
||||||
if self.callbacks.link_established != None:
|
rtt_data = umsgpack.packb(self.rtt)
|
||||||
thread = threading.Thread(target=self.callbacks.link_established, args=(self,))
|
rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT)
|
||||||
thread.daemon = True
|
rtt_packet.send()
|
||||||
thread.start()
|
self.had_outbound()
|
||||||
else:
|
|
||||||
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
|
if self.callbacks.link_established != None:
|
||||||
|
thread = threading.Thread(target=self.callbacks.link_established, args=(self,))
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
else:
|
||||||
|
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.status = Link.CLOSED
|
||||||
|
RNS.log("An error ocurred while validating link request proof on "+str(self)+".", RNS.LOG_ERROR)
|
||||||
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|
||||||
def identify(self, identity):
|
def identify(self, identity):
|
||||||
@@ -307,7 +319,7 @@ class Link:
|
|||||||
|
|
||||||
:param identity: An RNS.Identity instance to identify as.
|
:param identity: An RNS.Identity instance to identify as.
|
||||||
"""
|
"""
|
||||||
if self.initiator:
|
if self.initiator and self.status == Link.ACTIVE:
|
||||||
signed_data = self.link_id + identity.get_public_key()
|
signed_data = self.link_id + identity.get_public_key()
|
||||||
signature = identity.sign(signed_data)
|
signature = identity.sign(signed_data)
|
||||||
proof_data = identity.get_public_key() + signature
|
proof_data = identity.get_public_key() + signature
|
||||||
@@ -498,7 +510,11 @@ class Link:
|
|||||||
def __watchdog_job(self):
|
def __watchdog_job(self):
|
||||||
while not self.status == Link.CLOSED:
|
while not self.status == Link.CLOSED:
|
||||||
while (self.watchdog_lock):
|
while (self.watchdog_lock):
|
||||||
sleep(max(self.rtt, 0.025))
|
rtt_wait = 0.025
|
||||||
|
if hasattr(self, "rtt") and self.rtt:
|
||||||
|
rtt_wait = self.rtt
|
||||||
|
|
||||||
|
sleep(max(rtt_wait, 0.025))
|
||||||
|
|
||||||
if not self.status == Link.CLOSED:
|
if not self.status == Link.CLOSED:
|
||||||
# Link was initiated, but no response
|
# Link was initiated, but no response
|
||||||
@@ -517,16 +533,16 @@ class Link:
|
|||||||
next_check = self.request_time + self.establishment_timeout
|
next_check = self.request_time + self.establishment_timeout
|
||||||
sleep_time = next_check - time.time()
|
sleep_time = next_check - time.time()
|
||||||
if time.time() >= self.request_time + self.establishment_timeout:
|
if time.time() >= self.request_time + self.establishment_timeout:
|
||||||
if self.initiator:
|
|
||||||
RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG)
|
|
||||||
else:
|
|
||||||
RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG)
|
|
||||||
|
|
||||||
self.status = Link.CLOSED
|
self.status = Link.CLOSED
|
||||||
self.teardown_reason = Link.TIMEOUT
|
self.teardown_reason = Link.TIMEOUT
|
||||||
self.link_closed()
|
self.link_closed()
|
||||||
sleep_time = 0.001
|
sleep_time = 0.001
|
||||||
|
|
||||||
|
if self.initiator:
|
||||||
|
RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG)
|
||||||
|
else:
|
||||||
|
RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
elif self.status == Link.ACTIVE:
|
elif self.status == Link.ACTIVE:
|
||||||
activated_at = self.activated_at if self.activated_at != None else 0
|
activated_at = self.activated_at if self.activated_at != None else 0
|
||||||
last_inbound = max(max(self.last_inbound, self.last_proof), activated_at)
|
last_inbound = max(max(self.last_inbound, self.last_proof), activated_at)
|
||||||
@@ -844,7 +860,7 @@ class Link:
|
|||||||
try:
|
try:
|
||||||
self.fernet = Fernet(self.derived_key)
|
self.fernet = Fernet(self.derived_key)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not "+str(self)+" instantiate Fernet while performin encryption on link. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Could not instantiate Fernet while performin encryption on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
return self.fernet.encrypt(plaintext)
|
return self.fernet.encrypt(plaintext)
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,7 +30,7 @@ class Packet:
|
|||||||
"""
|
"""
|
||||||
The Packet class is used to create packet instances that can be sent
|
The Packet class is used to create packet instances that can be sent
|
||||||
over a Reticulum network. Packets will automatically be encrypted if
|
over a Reticulum network. Packets will automatically be encrypted if
|
||||||
they are adressed to a ``RNS.Destination.SINGLE`` destination,
|
they are addressed to a ``RNS.Destination.SINGLE`` destination,
|
||||||
``RNS.Destination.GROUP`` destination or a :ref:`RNS.Link<api-link>`.
|
``RNS.Destination.GROUP`` destination or a :ref:`RNS.Link<api-link>`.
|
||||||
|
|
||||||
For ``RNS.Destination.GROUP`` destinations, Reticulum will use the
|
For ``RNS.Destination.GROUP`` destinations, Reticulum will use the
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
class Resolver:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_identity(full_name):
|
||||||
|
pass
|
||||||
+5
-2
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -476,7 +476,8 @@ class Resource:
|
|||||||
|
|
||||||
if sleep_time < 0:
|
if sleep_time < 0:
|
||||||
if self.retries_left > 0:
|
if self.retries_left > 0:
|
||||||
RNS.log("Timed out waiting for parts, requesting retry", RNS.LOG_DEBUG)
|
ms = "" if self.outstanding_parts == 1 else "s"
|
||||||
|
RNS.log("Timed out waiting for "+str(self.outstanding_parts)+" part"+ms+", requesting retry", RNS.LOG_DEBUG)
|
||||||
if self.window > self.window_min:
|
if self.window > self.window_min:
|
||||||
self.window -= 1
|
self.window -= 1
|
||||||
if self.window_max > self.window_min:
|
if self.window_max > self.window_min:
|
||||||
@@ -661,6 +662,7 @@ class Resource:
|
|||||||
for map_hash in self.hashmap[self.consecutive_completed_height:self.consecutive_completed_height+self.window]:
|
for map_hash in self.hashmap[self.consecutive_completed_height:self.consecutive_completed_height+self.window]:
|
||||||
if map_hash == part_hash:
|
if map_hash == part_hash:
|
||||||
if self.parts[i] == None:
|
if self.parts[i] == None:
|
||||||
|
|
||||||
# Insert data into parts list
|
# Insert data into parts list
|
||||||
self.parts[i] = part_data
|
self.parts[i] = part_data
|
||||||
self.rtt_rxd_bytes += len(part_data)
|
self.rtt_rxd_bytes += len(part_data)
|
||||||
@@ -760,6 +762,7 @@ class Resource:
|
|||||||
self.req_sent = self.last_activity
|
self.req_sent = self.last_activity
|
||||||
self.req_sent_bytes = len(request_packet.raw)
|
self.req_sent_bytes = len(request_packet.raw)
|
||||||
self.req_resp = None
|
self.req_resp = None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG)
|
RNS.log("Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
||||||
|
|||||||
+94
-5
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -133,6 +133,7 @@ class Reticulum:
|
|||||||
JOB_INTERVAL = 5*60
|
JOB_INTERVAL = 5*60
|
||||||
CLEAN_INTERVAL = 15*60
|
CLEAN_INTERVAL = 15*60
|
||||||
PERSIST_INTERVAL = 60*60*12
|
PERSIST_INTERVAL = 60*60*12
|
||||||
|
GRACIOUS_PERSIST_INTERVAL = 60*5
|
||||||
|
|
||||||
router = None
|
router = None
|
||||||
config = None
|
config = None
|
||||||
@@ -200,6 +201,7 @@ class Reticulum:
|
|||||||
|
|
||||||
Reticulum.__transport_enabled = False
|
Reticulum.__transport_enabled = False
|
||||||
Reticulum.__use_implicit_proof = True
|
Reticulum.__use_implicit_proof = True
|
||||||
|
Reticulum.__allow_probes = False
|
||||||
|
|
||||||
Reticulum.panic_on_interface_error = False
|
Reticulum.panic_on_interface_error = False
|
||||||
|
|
||||||
@@ -207,6 +209,7 @@ class Reticulum:
|
|||||||
self.local_control_port = 37429
|
self.local_control_port = 37429
|
||||||
self.share_instance = True
|
self.share_instance = True
|
||||||
self.rpc_listener = None
|
self.rpc_listener = None
|
||||||
|
self.rpc_key = None
|
||||||
|
|
||||||
self.ifac_salt = Reticulum.IFAC_SALT
|
self.ifac_salt = Reticulum.IFAC_SALT
|
||||||
|
|
||||||
@@ -260,7 +263,8 @@ class Reticulum:
|
|||||||
RNS.Transport.start(self)
|
RNS.Transport.start(self)
|
||||||
|
|
||||||
self.rpc_addr = ("127.0.0.1", self.local_control_port)
|
self.rpc_addr = ("127.0.0.1", self.local_control_port)
|
||||||
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
if self.rpc_key == None:
|
||||||
|
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
||||||
|
|
||||||
if self.is_shared_instance:
|
if self.is_shared_instance:
|
||||||
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
|
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
|
||||||
@@ -288,7 +292,6 @@ class Reticulum:
|
|||||||
|
|
||||||
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
|
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
|
||||||
self.__persist_data()
|
self.__persist_data()
|
||||||
self.last_data_persist = time.time()
|
|
||||||
|
|
||||||
time.sleep(Reticulum.JOB_INTERVAL)
|
time.sleep(Reticulum.JOB_INTERVAL)
|
||||||
|
|
||||||
@@ -319,6 +322,7 @@ class Reticulum:
|
|||||||
self.is_standalone_instance = False
|
self.is_standalone_instance = False
|
||||||
self.is_connected_to_shared_instance = True
|
self.is_connected_to_shared_instance = True
|
||||||
Reticulum.__transport_enabled = False
|
Reticulum.__transport_enabled = False
|
||||||
|
Reticulum.__allow_probes = False
|
||||||
RNS.log("Connected to locally available Reticulum instance via: "+str(interface), RNS.LOG_DEBUG)
|
RNS.log("Connected to locally available Reticulum instance via: "+str(interface), RNS.LOG_DEBUG)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
|
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
|
||||||
@@ -357,10 +361,21 @@ class Reticulum:
|
|||||||
if option == "instance_control_port":
|
if option == "instance_control_port":
|
||||||
value = int(self.config["reticulum"][option])
|
value = int(self.config["reticulum"][option])
|
||||||
self.local_control_port = value
|
self.local_control_port = value
|
||||||
|
if option == "rpc_key":
|
||||||
|
try:
|
||||||
|
value = bytes.fromhex(self.config["reticulum"][option])
|
||||||
|
self.rpc_key = value
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Invalid shared instance RPC key specified, falling back to default key", RNS.LOG_ERROR)
|
||||||
|
self.rpc_key = None
|
||||||
if option == "enable_transport":
|
if option == "enable_transport":
|
||||||
v = self.config["reticulum"].as_bool(option)
|
v = self.config["reticulum"].as_bool(option)
|
||||||
if v == True:
|
if v == True:
|
||||||
Reticulum.__transport_enabled = True
|
Reticulum.__transport_enabled = True
|
||||||
|
if option == "respond_to_probes":
|
||||||
|
v = self.config["reticulum"].as_bool(option)
|
||||||
|
if v == True:
|
||||||
|
Reticulum.__allow_probes = True
|
||||||
if option == "panic_on_interface_error":
|
if option == "panic_on_interface_error":
|
||||||
v = self.config["reticulum"].as_bool(option)
|
v = self.config["reticulum"].as_bool(option)
|
||||||
if v == True:
|
if v == True:
|
||||||
@@ -441,6 +456,23 @@ class Reticulum:
|
|||||||
if c["pass_phrase"] != "":
|
if c["pass_phrase"] != "":
|
||||||
ifac_netkey = c["pass_phrase"]
|
ifac_netkey = c["pass_phrase"]
|
||||||
|
|
||||||
|
ingress_control = True
|
||||||
|
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
|
||||||
|
ic_max_held_announces = None
|
||||||
|
if "ic_max_held_announces" in c: ic_max_held_announces = c.as_int("ic_max_held_announces")
|
||||||
|
ic_burst_hold = None
|
||||||
|
if "ic_burst_hold" in c: ic_burst_hold = c.as_float("ic_burst_hold")
|
||||||
|
ic_burst_freq_new = None
|
||||||
|
if "ic_burst_freq_new" in c: ic_burst_freq_new = c.as_float("ic_burst_freq_new")
|
||||||
|
ic_burst_freq = None
|
||||||
|
if "ic_burst_freq" in c: ic_burst_freq = c.as_float("ic_burst_freq")
|
||||||
|
ic_new_time = None
|
||||||
|
if "ic_new_time" in c: ic_new_time = c.as_float("ic_new_time")
|
||||||
|
ic_burst_penalty = None
|
||||||
|
if "ic_burst_penalty" in c: ic_burst_penalty = c.as_float("ic_burst_penalty")
|
||||||
|
ic_held_release_interval = None
|
||||||
|
if "ic_held_release_interval" in c: ic_held_release_interval = c.as_float("ic_held_release_interval")
|
||||||
|
|
||||||
configured_bitrate = None
|
configured_bitrate = None
|
||||||
if "bitrate" in c:
|
if "bitrate" in c:
|
||||||
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
|
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
|
||||||
@@ -835,6 +867,8 @@ class Reticulum:
|
|||||||
flow_control = c.as_bool("flow_control") if "flow_control" in c else False
|
flow_control = c.as_bool("flow_control") if "flow_control" in c else False
|
||||||
id_interval = int(c["id_interval"]) if "id_interval" in c else None
|
id_interval = int(c["id_interval"]) if "id_interval" in c else None
|
||||||
id_callsign = c["id_callsign"] if "id_callsign" in c else None
|
id_callsign = c["id_callsign"] if "id_callsign" in c else None
|
||||||
|
st_alock = float(c["airtime_limit_short"]) if "airtime_limit_short" in c else None
|
||||||
|
lt_alock = float(c["airtime_limit_long"]) if "airtime_limit_long" in c else None
|
||||||
|
|
||||||
port = c["port"] if "port" in c else None
|
port = c["port"] if "port" in c else None
|
||||||
|
|
||||||
@@ -852,7 +886,9 @@ class Reticulum:
|
|||||||
cr = codingrate,
|
cr = codingrate,
|
||||||
flow_control = flow_control,
|
flow_control = flow_control,
|
||||||
id_interval = id_interval,
|
id_interval = id_interval,
|
||||||
id_callsign = id_callsign
|
id_callsign = id_callsign,
|
||||||
|
st_alock = st_alock,
|
||||||
|
lt_alock = lt_alock
|
||||||
)
|
)
|
||||||
|
|
||||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||||
@@ -874,6 +910,14 @@ class Reticulum:
|
|||||||
interface.announce_rate_target = announce_rate_target
|
interface.announce_rate_target = announce_rate_target
|
||||||
interface.announce_rate_grace = announce_rate_grace
|
interface.announce_rate_grace = announce_rate_grace
|
||||||
interface.announce_rate_penalty = announce_rate_penalty
|
interface.announce_rate_penalty = announce_rate_penalty
|
||||||
|
interface.ingress_control = ingress_control
|
||||||
|
if ic_max_held_announces != None: interface.ic_max_held_announces = ic_max_held_announces
|
||||||
|
if ic_burst_hold != None: interface.ic_burst_hold = ic_burst_hold
|
||||||
|
if ic_burst_freq_new != None: interface.ic_burst_freq_new = ic_burst_freq_new
|
||||||
|
if ic_burst_freq != None: interface.ic_burst_freq = ic_burst_freq
|
||||||
|
if ic_new_time != None: interface.ic_new_time = ic_new_time
|
||||||
|
if ic_burst_penalty != None: interface.ic_burst_penalty = ic_burst_penalty
|
||||||
|
if ic_held_release_interval != None: interface.ic_held_release_interval = ic_held_release_interval
|
||||||
|
|
||||||
interface.ifac_netname = ifac_netname
|
interface.ifac_netname = ifac_netname
|
||||||
interface.ifac_netkey = ifac_netkey
|
interface.ifac_netkey = ifac_netkey
|
||||||
@@ -960,11 +1004,13 @@ class Reticulum:
|
|||||||
RNS.Transport.interfaces.append(interface)
|
RNS.Transport.interfaces.append(interface)
|
||||||
|
|
||||||
def _should_persist_data(self):
|
def _should_persist_data(self):
|
||||||
self.__persist_data()
|
if time.time() > self.last_data_persist+Reticulum.GRACIOUS_PERSIST_INTERVAL:
|
||||||
|
self.__persist_data()
|
||||||
|
|
||||||
def __persist_data(self):
|
def __persist_data(self):
|
||||||
RNS.Transport.persist_data()
|
RNS.Transport.persist_data()
|
||||||
RNS.Identity.persist_data()
|
RNS.Identity.persist_data()
|
||||||
|
self.last_data_persist = time.time()
|
||||||
|
|
||||||
def __clean_caches(self):
|
def __clean_caches(self):
|
||||||
RNS.log("Cleaning resource and packet caches...", RNS.LOG_EXTREME)
|
RNS.log("Cleaning resource and packet caches...", RNS.LOG_EXTREME)
|
||||||
@@ -1040,6 +1086,9 @@ class Reticulum:
|
|||||||
if path == "path":
|
if path == "path":
|
||||||
rpc_connection.send(self.drop_path(call["destination_hash"]))
|
rpc_connection.send(self.drop_path(call["destination_hash"]))
|
||||||
|
|
||||||
|
if path == "all_via":
|
||||||
|
rpc_connection.send(self.drop_all_via(call["destination_hash"]))
|
||||||
|
|
||||||
if path == "announce_queues":
|
if path == "announce_queues":
|
||||||
rpc_connection.send(self.drop_announce_queues())
|
rpc_connection.send(self.drop_announce_queues())
|
||||||
|
|
||||||
@@ -1089,6 +1138,18 @@ class Reticulum:
|
|||||||
else:
|
else:
|
||||||
ifstats["tunnelstate"] = None
|
ifstats["tunnelstate"] = None
|
||||||
|
|
||||||
|
if hasattr(interface, "r_airtime_short"):
|
||||||
|
ifstats["airtime_short"] = interface.r_airtime_short
|
||||||
|
|
||||||
|
if hasattr(interface, "r_airtime_long"):
|
||||||
|
ifstats["airtime_long"] = interface.r_airtime_long
|
||||||
|
|
||||||
|
if hasattr(interface, "r_channel_load_short"):
|
||||||
|
ifstats["channel_load_short"] = interface.r_channel_load_short
|
||||||
|
|
||||||
|
if hasattr(interface, "r_channel_load_long"):
|
||||||
|
ifstats["channel_load_long"] = interface.r_channel_load_long
|
||||||
|
|
||||||
if hasattr(interface, "bitrate"):
|
if hasattr(interface, "bitrate"):
|
||||||
if interface.bitrate != None:
|
if interface.bitrate != None:
|
||||||
ifstats["bitrate"] = interface.bitrate
|
ifstats["bitrate"] = interface.bitrate
|
||||||
@@ -1119,6 +1180,9 @@ class Reticulum:
|
|||||||
ifstats["name"] = str(interface)
|
ifstats["name"] = str(interface)
|
||||||
ifstats["rxb"] = interface.rxb
|
ifstats["rxb"] = interface.rxb
|
||||||
ifstats["txb"] = interface.txb
|
ifstats["txb"] = interface.txb
|
||||||
|
ifstats["incoming_announce_frequency"] = interface.incoming_announce_frequency()
|
||||||
|
ifstats["outgoing_announce_frequency"] = interface.outgoing_announce_frequency()
|
||||||
|
ifstats["held_announces"] = len(interface.held_announces)
|
||||||
ifstats["status"] = interface.online
|
ifstats["status"] = interface.online
|
||||||
ifstats["mode"] = interface.mode
|
ifstats["mode"] = interface.mode
|
||||||
|
|
||||||
@@ -1128,6 +1192,11 @@ class Reticulum:
|
|||||||
stats["interfaces"] = interfaces
|
stats["interfaces"] = interfaces
|
||||||
if Reticulum.transport_enabled():
|
if Reticulum.transport_enabled():
|
||||||
stats["transport_id"] = RNS.Transport.identity.hash
|
stats["transport_id"] = RNS.Transport.identity.hash
|
||||||
|
stats["transport_uptime"] = time.time()-RNS.Transport.start_time
|
||||||
|
if Reticulum.probe_destination_enabled():
|
||||||
|
stats["probe_responder"] = RNS.Transport.probe_destination.hash
|
||||||
|
else:
|
||||||
|
stats["probe_responder"] = None
|
||||||
|
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
@@ -1184,6 +1253,22 @@ class Reticulum:
|
|||||||
else:
|
else:
|
||||||
return RNS.Transport.expire_path(destination)
|
return RNS.Transport.expire_path(destination)
|
||||||
|
|
||||||
|
def drop_all_via(self, transport_hash):
|
||||||
|
if self.is_connected_to_shared_instance:
|
||||||
|
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||||
|
rpc_connection.send({"drop": "all_via", "destination_hash": transport_hash})
|
||||||
|
response = rpc_connection.recv()
|
||||||
|
return response
|
||||||
|
|
||||||
|
else:
|
||||||
|
dropped_count = 0
|
||||||
|
for destination_hash in RNS.Transport.destination_table:
|
||||||
|
if RNS.Transport.destination_table[destination_hash][1] == transport_hash:
|
||||||
|
RNS.Transport.expire_path(destination_hash)
|
||||||
|
dropped_count += 1
|
||||||
|
|
||||||
|
return dropped_count
|
||||||
|
|
||||||
def drop_announce_queues(self):
|
def drop_announce_queues(self):
|
||||||
if self.is_connected_to_shared_instance:
|
if self.is_connected_to_shared_instance:
|
||||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||||
@@ -1266,6 +1351,10 @@ class Reticulum:
|
|||||||
"""
|
"""
|
||||||
return Reticulum.__transport_enabled
|
return Reticulum.__transport_enabled
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def probe_destination_enabled():
|
||||||
|
return Reticulum.__allow_probes
|
||||||
|
|
||||||
# Default configuration file:
|
# Default configuration file:
|
||||||
__default_rns_config__ = '''# This is the default Reticulum config file.
|
__default_rns_config__ = '''# This is the default Reticulum config file.
|
||||||
# You should probably edit it to include any additional,
|
# You should probably edit it to include any additional,
|
||||||
|
|||||||
+124
-47
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -115,9 +115,10 @@ class Transport:
|
|||||||
|
|
||||||
pending_local_path_requests = {}
|
pending_local_path_requests = {}
|
||||||
|
|
||||||
jobs_locked = False
|
start_time = None
|
||||||
jobs_running = False
|
jobs_locked = False
|
||||||
job_interval = 0.250
|
jobs_running = False
|
||||||
|
job_interval = 0.250
|
||||||
links_last_checked = 0.0
|
links_last_checked = 0.0
|
||||||
links_check_interval = 1.0
|
links_check_interval = 1.0
|
||||||
receipts_last_checked = 0.0
|
receipts_last_checked = 0.0
|
||||||
@@ -127,6 +128,8 @@ class Transport:
|
|||||||
hashlist_maxsize = 1000000
|
hashlist_maxsize = 1000000
|
||||||
tables_last_culled = 0.0
|
tables_last_culled = 0.0
|
||||||
tables_cull_interval = 5.0
|
tables_cull_interval = 5.0
|
||||||
|
interface_last_jobs = 0.0
|
||||||
|
interface_jobs_interval = 5.0
|
||||||
|
|
||||||
identity = None
|
identity = None
|
||||||
|
|
||||||
@@ -269,7 +272,17 @@ class Transport:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not load tunnel table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Could not load tunnel table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
if RNS.Reticulum.probe_destination_enabled():
|
||||||
|
Transport.probe_destination = RNS.Destination(Transport.identity, RNS.Destination.IN, RNS.Destination.SINGLE, Transport.APP_NAME, "probe")
|
||||||
|
Transport.probe_destination.accepts_links(False)
|
||||||
|
Transport.probe_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||||
|
Transport.probe_destination.announce()
|
||||||
|
RNS.log("Transport Instance will respond to probe requests on "+str(Transport.probe_destination), RNS.LOG_NOTICE)
|
||||||
|
else:
|
||||||
|
Transport.probe_destination = None
|
||||||
|
|
||||||
RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE)
|
RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE)
|
||||||
|
Transport.start_time = time.time()
|
||||||
|
|
||||||
# Synthesize tunnels for any interfaces wanting it
|
# Synthesize tunnels for any interfaces wanting it
|
||||||
for interface in Transport.interfaces:
|
for interface in Transport.interfaces:
|
||||||
@@ -334,18 +347,19 @@ class Transport:
|
|||||||
for receipt in Transport.receipts:
|
for receipt in Transport.receipts:
|
||||||
receipt.check_timeout()
|
receipt.check_timeout()
|
||||||
if receipt.status != RNS.PacketReceipt.SENT:
|
if receipt.status != RNS.PacketReceipt.SENT:
|
||||||
Transport.receipts.remove(receipt)
|
if receipt in Transport.receipts:
|
||||||
|
Transport.receipts.remove(receipt)
|
||||||
|
|
||||||
Transport.receipts_last_checked = time.time()
|
Transport.receipts_last_checked = time.time()
|
||||||
|
|
||||||
# Process announces needing retransmission
|
# Process announces needing retransmission
|
||||||
if time.time() > Transport.announces_last_checked+Transport.announces_check_interval:
|
if time.time() > Transport.announces_last_checked+Transport.announces_check_interval:
|
||||||
|
completed_announces = []
|
||||||
for destination_hash in Transport.announce_table:
|
for destination_hash in Transport.announce_table:
|
||||||
announce_entry = Transport.announce_table[destination_hash]
|
announce_entry = Transport.announce_table[destination_hash]
|
||||||
if announce_entry[2] > Transport.PATHFINDER_R:
|
if announce_entry[2] > Transport.PATHFINDER_R:
|
||||||
RNS.log("Completed announce processing for "+RNS.prettyhexrep(destination_hash)+", retry limit reached", RNS.LOG_EXTREME)
|
RNS.log("Completed announce processing for "+RNS.prettyhexrep(destination_hash)+", retry limit reached", RNS.LOG_EXTREME)
|
||||||
Transport.announce_table.pop(destination_hash)
|
completed_announces.append(destination_hash)
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
if time.time() > announce_entry[1]:
|
if time.time() > announce_entry[1]:
|
||||||
announce_entry[1] = time.time() + Transport.PATHFINDER_G + Transport.PATHFINDER_RW
|
announce_entry[1] = time.time() + Transport.PATHFINDER_G + Transport.PATHFINDER_RW
|
||||||
@@ -392,6 +406,10 @@ class Transport:
|
|||||||
Transport.announce_table[destination_hash] = held_entry
|
Transport.announce_table[destination_hash] = held_entry
|
||||||
RNS.log("Reinserting held announce into table", RNS.LOG_DEBUG)
|
RNS.log("Reinserting held announce into table", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
for destination_hash in completed_announces:
|
||||||
|
if destination_hash in Transport.announce_table:
|
||||||
|
Transport.announce_table.pop(destination_hash)
|
||||||
|
|
||||||
Transport.announces_last_checked = time.time()
|
Transport.announces_last_checked = time.time()
|
||||||
|
|
||||||
|
|
||||||
@@ -427,12 +445,41 @@ class Transport:
|
|||||||
if link_entry[6] in Transport.path_requests:
|
if link_entry[6] in Transport.path_requests:
|
||||||
last_path_request = Transport.path_requests[link_entry[6]]
|
last_path_request = Transport.path_requests[link_entry[6]]
|
||||||
|
|
||||||
|
lr_taken_hops = link_entry[5]
|
||||||
|
|
||||||
|
path_request_throttle = time.time() - last_path_request < Transport.PATH_REQUEST_MI
|
||||||
|
path_request_conditions = False
|
||||||
|
|
||||||
|
# If the path has been invalidated between the time of
|
||||||
|
# making the link request and now, try to rediscover it
|
||||||
|
if not Transport.has_path(link_entry[6]):
|
||||||
|
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and path is now missing", RNS.LOG_DEBUG)
|
||||||
|
path_request_conditions =True
|
||||||
|
|
||||||
# If this link request was originated from a local client
|
# If this link request was originated from a local client
|
||||||
# attempt to rediscover a path to the destination, if this
|
# attempt to rediscover a path to the destination, if this
|
||||||
# has not already happened recently.
|
# has not already happened recently.
|
||||||
lr_taken_hops = link_entry[5]
|
elif not path_request_throttle and lr_taken_hops == 0:
|
||||||
if lr_taken_hops == 0 and time.time() - last_path_request > Transport.PATH_REQUEST_MI:
|
|
||||||
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted local client link was never established", RNS.LOG_DEBUG)
|
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted local client link was never established", RNS.LOG_DEBUG)
|
||||||
|
path_request_conditions = True
|
||||||
|
|
||||||
|
# If the link destination was previously only 1 hop
|
||||||
|
# away, this likely means that it was local to one
|
||||||
|
# of our interfaces, and that it roamed somewhere else.
|
||||||
|
# In that case, try to discover a new path.
|
||||||
|
elif not path_request_throttle and Transport.hops_to(link_entry[6]) == 1:
|
||||||
|
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and destination was previously local to an interface on this instance", RNS.LOG_DEBUG)
|
||||||
|
path_request_conditions = True
|
||||||
|
|
||||||
|
# If the link destination was previously only 1 hop
|
||||||
|
# away, this likely means that it was local to one
|
||||||
|
# of our interfaces, and that it roamed somewhere else.
|
||||||
|
# In that case, try to discover a new path.
|
||||||
|
elif not path_request_throttle and lr_taken_hops == 1:
|
||||||
|
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and link initiator is local to an interface on this instance", RNS.LOG_DEBUG)
|
||||||
|
path_request_conditions = True
|
||||||
|
|
||||||
|
if path_request_conditions:
|
||||||
if not link_entry[6] in path_requests:
|
if not link_entry[6] in path_requests:
|
||||||
path_requests.append(link_entry[6])
|
path_requests.append(link_entry[6])
|
||||||
|
|
||||||
@@ -563,6 +610,11 @@ class Transport:
|
|||||||
|
|
||||||
Transport.tables_last_culled = time.time()
|
Transport.tables_last_culled = time.time()
|
||||||
|
|
||||||
|
if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
|
||||||
|
for interface in Transport.interfaces:
|
||||||
|
interface.process_held_announces()
|
||||||
|
Transport.interface_last_jobs = time.time()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Transport jobs were locked, do nothing
|
# Transport jobs were locked, do nothing
|
||||||
pass
|
pass
|
||||||
@@ -842,6 +894,8 @@ class Transport:
|
|||||||
# thread.start()
|
# thread.start()
|
||||||
|
|
||||||
Transport.transmit(interface, packet.raw)
|
Transport.transmit(interface, packet.raw)
|
||||||
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
|
interface.sent_announce()
|
||||||
sent = True
|
sent = True
|
||||||
|
|
||||||
if sent:
|
if sent:
|
||||||
@@ -995,6 +1049,7 @@ class Transport:
|
|||||||
|
|
||||||
packet = RNS.Packet(None, raw)
|
packet = RNS.Packet(None, raw)
|
||||||
if not packet.unpack():
|
if not packet.unpack():
|
||||||
|
Transport.jobs_locked = False
|
||||||
return
|
return
|
||||||
|
|
||||||
packet.receiving_interface = interface
|
packet.receiving_interface = interface
|
||||||
@@ -1008,7 +1063,7 @@ class Transport:
|
|||||||
Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi])
|
Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi])
|
||||||
|
|
||||||
while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
||||||
Transport.local_client_rssi_cache.pop()
|
Transport.local_client_rssi_cache.pop(0)
|
||||||
|
|
||||||
if hasattr(interface, "r_stat_snr"):
|
if hasattr(interface, "r_stat_snr"):
|
||||||
if interface.r_stat_rssi != None:
|
if interface.r_stat_rssi != None:
|
||||||
@@ -1017,7 +1072,7 @@ class Transport:
|
|||||||
Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr])
|
Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr])
|
||||||
|
|
||||||
while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
||||||
Transport.local_client_snr_cache.pop()
|
Transport.local_client_snr_cache.pop(0)
|
||||||
|
|
||||||
if len(Transport.local_client_interfaces) > 0:
|
if len(Transport.local_client_interfaces) > 0:
|
||||||
if Transport.is_local_client_interface(interface):
|
if Transport.is_local_client_interface(interface):
|
||||||
@@ -1076,6 +1131,7 @@ class Transport:
|
|||||||
# normal processing.
|
# normal processing.
|
||||||
if packet.context == RNS.Packet.CACHE_REQUEST:
|
if packet.context == RNS.Packet.CACHE_REQUEST:
|
||||||
if Transport.cache_request_packet(packet):
|
if Transport.cache_request_packet(packet):
|
||||||
|
Transport.jobs_locked = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# If the packet is in transport, check whether we
|
# If the packet is in transport, check whether we
|
||||||
@@ -1182,6 +1238,19 @@ class Transport:
|
|||||||
# announces, queueing rebroadcasts of these, and removal
|
# announces, queueing rebroadcasts of these, and removal
|
||||||
# of queued announce rebroadcasts once handed to the next node.
|
# of queued announce rebroadcasts once handed to the next node.
|
||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
|
if interface != None and RNS.Identity.validate_announce(packet, only_validate_signature=True):
|
||||||
|
interface.received_announce()
|
||||||
|
|
||||||
|
if not packet.destination_hash in Transport.destination_table:
|
||||||
|
# This is an unknown destination, and we'll apply
|
||||||
|
# potential ingress limiting. Already known
|
||||||
|
# destinations will have re-announces controlled
|
||||||
|
# by normal announce rate limiting.
|
||||||
|
if interface.should_ingress_limit():
|
||||||
|
interface.hold_announce(packet)
|
||||||
|
Transport.jobs_locked = False
|
||||||
|
return
|
||||||
|
|
||||||
local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None)
|
local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None)
|
||||||
if local_destination == None and RNS.Identity.validate_announce(packet):
|
if local_destination == None and RNS.Identity.validate_announce(packet):
|
||||||
if packet.transport_id != None:
|
if packet.transport_id != None:
|
||||||
@@ -1275,7 +1344,7 @@ class Transport:
|
|||||||
should_add = True
|
should_add = True
|
||||||
|
|
||||||
if should_add:
|
if should_add:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
|
||||||
rate_blocked = False
|
rate_blocked = False
|
||||||
if packet.context != RNS.Packet.PATH_RESPONSE and packet.receiving_interface.announce_rate_target != None:
|
if packet.context != RNS.Packet.PATH_RESPONSE and packet.receiving_interface.announce_rate_target != None:
|
||||||
@@ -1493,7 +1562,7 @@ class Transport:
|
|||||||
RNS.log("Error while processing external announce callback.", RNS.LOG_ERROR)
|
RNS.log("Error while processing external announce callback.", RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
# Handling for linkrequests to local destinations
|
# Handling for link requests to local destinations
|
||||||
elif packet.packet_type == RNS.Packet.LINKREQUEST:
|
elif packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||||
if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
|
if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
|
||||||
for destination in Transport.destinations:
|
for destination in Transport.destinations:
|
||||||
@@ -1740,6 +1809,8 @@ class Transport:
|
|||||||
def activate_link(link):
|
def activate_link(link):
|
||||||
RNS.log("Activating link "+str(link), RNS.LOG_EXTREME)
|
RNS.log("Activating link "+str(link), RNS.LOG_EXTREME)
|
||||||
if link in Transport.pending_links:
|
if link in Transport.pending_links:
|
||||||
|
if link.status != RNS.Link.ACTIVE:
|
||||||
|
raise IOError("Invalid link state for link activation: "+str(link.status))
|
||||||
Transport.pending_links.remove(link)
|
Transport.pending_links.remove(link)
|
||||||
Transport.active_links.append(link)
|
Transport.active_links.append(link)
|
||||||
link.status = RNS.Link.ACTIVE
|
link.status = RNS.Link.ACTIVE
|
||||||
@@ -1777,8 +1848,11 @@ class Transport:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def should_cache(packet):
|
def should_cache(packet):
|
||||||
if packet.context == RNS.Packet.RESOURCE_PRF:
|
# TODO: Rework the caching system. It's currently
|
||||||
return True
|
# not very useful to even cache Resource proofs,
|
||||||
|
# disabling it for now, until redesigned.
|
||||||
|
# if packet.context == RNS.Packet.RESOURCE_PRF:
|
||||||
|
# return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1801,8 +1875,7 @@ class Transport:
|
|||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error writing packet to cache", RNS.LOG_ERROR)
|
RNS.log("Error writing packet to cache. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_cached_packet(packet_hash):
|
def get_cached_packet(packet_hash):
|
||||||
@@ -2044,40 +2117,44 @@ class Transport:
|
|||||||
next_hop = Transport.destination_table[destination_hash][1]
|
next_hop = Transport.destination_table[destination_hash][1]
|
||||||
received_from = Transport.destination_table[destination_hash][5]
|
received_from = Transport.destination_table[destination_hash][5]
|
||||||
|
|
||||||
if requestor_transport_id != None and next_hop == requestor_transport_id:
|
if attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING and attached_interface == received_from:
|
||||||
# TODO: Find a bandwidth efficient way to invalidate our
|
RNS.log("Not answering path request on roaming-mode interface, since next hop is on same roaming-mode interface", RNS.LOG_DEBUG)
|
||||||
# known path on this signal. The obvious way of signing
|
|
||||||
# path requests with transport instance keys is quite
|
|
||||||
# inefficient. There is probably a better way. Doing
|
|
||||||
# path invalidation here would decrease the network
|
|
||||||
# convergence time. Maybe just drop it?
|
|
||||||
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG)
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG)
|
if requestor_transport_id != None and next_hop == requestor_transport_id:
|
||||||
|
# TODO: Find a bandwidth efficient way to invalidate our
|
||||||
now = time.time()
|
# known path on this signal. The obvious way of signing
|
||||||
retries = Transport.PATHFINDER_R
|
# path requests with transport instance keys is quite
|
||||||
local_rebroadcasts = 0
|
# inefficient. There is probably a better way. Doing
|
||||||
block_rebroadcasts = True
|
# path invalidation here would decrease the network
|
||||||
announce_hops = packet.hops
|
# convergence time. Maybe just drop it?
|
||||||
|
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG)
|
||||||
if is_from_local_client:
|
|
||||||
retransmit_timeout = now
|
|
||||||
else:
|
else:
|
||||||
# TODO: Look at this timing
|
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG)
|
||||||
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
|
|
||||||
|
|
||||||
# This handles an edge case where a peer sends a past
|
now = time.time()
|
||||||
# request for a destination just after an announce for
|
retries = Transport.PATHFINDER_R
|
||||||
# said destination has arrived, but before it has been
|
local_rebroadcasts = 0
|
||||||
# rebroadcast locally. In such a case the actual announce
|
block_rebroadcasts = True
|
||||||
# is temporarily held, and then reinserted when the path
|
announce_hops = packet.hops
|
||||||
# request has been served to the peer.
|
|
||||||
if packet.destination_hash in Transport.announce_table:
|
|
||||||
held_entry = Transport.announce_table[packet.destination_hash]
|
|
||||||
Transport.held_announces[packet.destination_hash] = held_entry
|
|
||||||
|
|
||||||
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
|
if is_from_local_client:
|
||||||
|
retransmit_timeout = now
|
||||||
|
else:
|
||||||
|
# TODO: Look at this timing
|
||||||
|
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||||
|
|
||||||
|
# This handles an edge case where a peer sends a past
|
||||||
|
# request for a destination just after an announce for
|
||||||
|
# said destination has arrived, but before it has been
|
||||||
|
# rebroadcast locally. In such a case the actual announce
|
||||||
|
# is temporarily held, and then reinserted when the path
|
||||||
|
# request has been served to the peer.
|
||||||
|
if packet.destination_hash in Transport.announce_table:
|
||||||
|
held_entry = Transport.announce_table[packet.destination_hash]
|
||||||
|
Transport.held_announces[packet.destination_hash] = held_entry
|
||||||
|
|
||||||
|
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
|
||||||
|
|
||||||
elif is_from_local_client:
|
elif is_from_local_client:
|
||||||
# Forward path request on all interfaces
|
# Forward path request on all interfaces
|
||||||
|
|||||||
+399
-44
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
import RNS
|
import RNS
|
||||||
import argparse
|
import argparse
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@@ -34,9 +35,12 @@ APP_NAME = "rncp"
|
|||||||
allow_all = False
|
allow_all = False
|
||||||
allowed_identity_hashes = []
|
allowed_identity_hashes = []
|
||||||
|
|
||||||
def receive(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False, limit = None, disable_auth = None, disable_announce = False):
|
def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False, limit = None, disable_auth = None, announce = False):
|
||||||
global allow_all, allowed_identity_hashes
|
global allow_all, allowed_identity_hashes
|
||||||
|
from tempfile import TemporaryFile
|
||||||
identity = None
|
identity = None
|
||||||
|
if announce < 0:
|
||||||
|
announce = False
|
||||||
|
|
||||||
targetloglevel = 3+verbosity-quietness
|
targetloglevel = 3+verbosity-quietness
|
||||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||||
@@ -54,16 +58,48 @@ def receive(configdir, verbosity = 0, quietness = 0, allowed = [], display_ident
|
|||||||
|
|
||||||
if display_identity:
|
if display_identity:
|
||||||
print("Identity : "+str(identity))
|
print("Identity : "+str(identity))
|
||||||
print("Receiving on : "+RNS.prettyhexrep(destination.hash))
|
print("Listening on : "+RNS.prettyhexrep(destination.hash))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
if disable_auth:
|
if disable_auth:
|
||||||
allow_all = True
|
allow_all = True
|
||||||
else:
|
else:
|
||||||
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
|
try:
|
||||||
|
allowed_file_name = "allowed_identities"
|
||||||
|
allowed_file = None
|
||||||
|
if os.path.isfile(os.path.expanduser("/etc/rncp/"+allowed_file_name)):
|
||||||
|
allowed_file = os.path.expanduser("/etc/rncp/"+allowed_file_name)
|
||||||
|
elif os.path.isfile(os.path.expanduser("~/.config/rncp/"+allowed_file_name)):
|
||||||
|
allowed_file = os.path.expanduser("~/.config/rncp/"+allowed_file_name)
|
||||||
|
elif os.path.isfile(os.path.expanduser("~/.rncp/"+allowed_file_name)):
|
||||||
|
allowed_file = os.path.expanduser("~/.rncp/"+allowed_file_name)
|
||||||
|
if allowed_file != None:
|
||||||
|
af = open(allowed_file, "r")
|
||||||
|
al = af.read().replace("\r", "").split("\n")
|
||||||
|
ali = []
|
||||||
|
for a in al:
|
||||||
|
if len(a) == dest_len:
|
||||||
|
ali.append(a)
|
||||||
|
|
||||||
|
if len(ali) > 0:
|
||||||
|
if not allowed:
|
||||||
|
allowed = ali
|
||||||
|
else:
|
||||||
|
allowed.extend(ali)
|
||||||
|
if len(ali) == 1:
|
||||||
|
ms = "y"
|
||||||
|
else:
|
||||||
|
ms = "ies"
|
||||||
|
|
||||||
|
RNS.log("Loaded "+str(len(ali))+" allowed identit"+ms+" from "+str(allowed_file), RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Error while parsing allowed_identities file. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
if allowed != None:
|
if allowed != None:
|
||||||
for a in allowed:
|
for a in allowed:
|
||||||
try:
|
try:
|
||||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
|
||||||
if len(a) != dest_len:
|
if len(a) != dest_len:
|
||||||
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||||
try:
|
try:
|
||||||
@@ -78,16 +114,60 @@ def receive(configdir, verbosity = 0, quietness = 0, allowed = [], display_ident
|
|||||||
if len(allowed_identity_hashes) < 1 and not disable_auth:
|
if len(allowed_identity_hashes) < 1 and not disable_auth:
|
||||||
print("Warning: No allowed identities configured, rncp will not accept any files!")
|
print("Warning: No allowed identities configured, rncp will not accept any files!")
|
||||||
|
|
||||||
destination.set_link_established_callback(receive_link_established)
|
def fetch_request(path, data, request_id, link_id, remote_identity, requested_at):
|
||||||
print("rncp ready to receive on "+RNS.prettyhexrep(destination.hash))
|
target_link = None
|
||||||
|
for link in RNS.Transport.active_links:
|
||||||
|
if link.link_id == link_id:
|
||||||
|
target_link = link
|
||||||
|
|
||||||
if not disable_announce:
|
file_path = os.path.expanduser(data)
|
||||||
destination.announce()
|
if not os.path.isfile(file_path):
|
||||||
|
RNS.log("Client-requested file not found: "+str(file_path), RNS.LOG_VERBOSE)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if target_link != None:
|
||||||
|
RNS.log("Sending file "+str(file_path)+" to client", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
|
temp_file = TemporaryFile()
|
||||||
|
real_file = open(file_path, "rb")
|
||||||
|
filename_bytes = os.path.basename(file_path).encode("utf-8")
|
||||||
|
filename_len = len(filename_bytes)
|
||||||
|
|
||||||
|
if filename_len > 0xFFFF:
|
||||||
|
print("Filename exceeds max size, cannot send")
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
print("Preparing file...", end=" ")
|
||||||
|
|
||||||
|
temp_file.write(filename_len.to_bytes(2, "big"))
|
||||||
|
temp_file.write(filename_bytes)
|
||||||
|
temp_file.write(real_file.read())
|
||||||
|
temp_file.seek(0)
|
||||||
|
|
||||||
|
fetch_resource = RNS.Resource(temp_file, target_link)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
destination.set_link_established_callback(client_link_established)
|
||||||
|
destination.register_request_handler("fetch_file", response_generator=fetch_request, allow=RNS.Destination.ALLOW_LIST, allowed_list=allowed_identity_hashes)
|
||||||
|
print("rncp listening on "+RNS.prettyhexrep(destination.hash))
|
||||||
|
|
||||||
|
if announce >= 0:
|
||||||
|
def job():
|
||||||
|
destination.announce()
|
||||||
|
if announce > 0:
|
||||||
|
while True:
|
||||||
|
time.sleep(announce)
|
||||||
|
destination.announce()
|
||||||
|
|
||||||
|
threading.Thread(target=job, daemon=True).start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def receive_link_established(link):
|
def client_link_established(link):
|
||||||
RNS.log("Incoming link established", RNS.LOG_VERBOSE)
|
RNS.log("Incoming link established", RNS.LOG_VERBOSE)
|
||||||
link.set_remote_identified_callback(receive_sender_identified)
|
link.set_remote_identified_callback(receive_sender_identified)
|
||||||
link.set_resource_strategy(RNS.Link.ACCEPT_APP)
|
link.set_resource_strategy(RNS.Link.ACCEPT_APP)
|
||||||
@@ -181,7 +261,221 @@ def sender_progress(resource):
|
|||||||
resource_done = True
|
resource_done = True
|
||||||
|
|
||||||
link = None
|
link = None
|
||||||
def send(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT):
|
def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False):
|
||||||
|
global current_resource, resource_done, link, speed
|
||||||
|
targetloglevel = 3+verbosity-quietness
|
||||||
|
|
||||||
|
try:
|
||||||
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
|
if len(destination) != dest_len:
|
||||||
|
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||||
|
try:
|
||||||
|
destination_hash = bytes.fromhex(destination)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError("Invalid destination entered. Check your input.")
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||||
|
|
||||||
|
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
||||||
|
if os.path.isfile(identity_path):
|
||||||
|
identity = RNS.Identity.from_file(identity_path)
|
||||||
|
if identity == None:
|
||||||
|
RNS.log("Could not load identity for rncp. The identity file at \""+str(identity_path)+"\" may be corrupt or unreadable.", RNS.LOG_ERROR)
|
||||||
|
exit(2)
|
||||||
|
else:
|
||||||
|
identity = None
|
||||||
|
|
||||||
|
if identity == None:
|
||||||
|
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||||
|
identity = RNS.Identity()
|
||||||
|
identity.to_file(identity_path)
|
||||||
|
|
||||||
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
|
RNS.Transport.request_path(destination_hash)
|
||||||
|
if silent:
|
||||||
|
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested")
|
||||||
|
else:
|
||||||
|
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||||
|
estab_timeout = time.time()+timeout
|
||||||
|
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
|
||||||
|
if not silent:
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
|
if silent:
|
||||||
|
print("Path not found")
|
||||||
|
else:
|
||||||
|
print("\r \rPath not found")
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
if silent:
|
||||||
|
print("Establishing link with "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
|
||||||
|
|
||||||
|
listener_identity = RNS.Identity.recall(destination_hash)
|
||||||
|
listener_destination = RNS.Destination(
|
||||||
|
listener_identity,
|
||||||
|
RNS.Destination.OUT,
|
||||||
|
RNS.Destination.SINGLE,
|
||||||
|
APP_NAME,
|
||||||
|
"receive"
|
||||||
|
)
|
||||||
|
|
||||||
|
link = RNS.Link(listener_destination)
|
||||||
|
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
|
||||||
|
if not silent:
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
|
if silent:
|
||||||
|
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \rCould not establish link with "+RNS.prettyhexrep(destination_hash))
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
if silent:
|
||||||
|
print("Requesting file from remote...")
|
||||||
|
else:
|
||||||
|
print("\r \rRequesting file from remote ", end=" ")
|
||||||
|
|
||||||
|
link.identify(identity)
|
||||||
|
|
||||||
|
request_resolved = False
|
||||||
|
request_status = "unknown"
|
||||||
|
resource_resolved = False
|
||||||
|
resource_status = "unrequested"
|
||||||
|
current_resource = None
|
||||||
|
def request_response(request_receipt):
|
||||||
|
nonlocal request_resolved, request_status
|
||||||
|
if request_receipt.response == False:
|
||||||
|
request_status = "not_found"
|
||||||
|
elif request_receipt.response == None:
|
||||||
|
request_status = "remote_error"
|
||||||
|
else:
|
||||||
|
request_status = "found"
|
||||||
|
|
||||||
|
request_resolved = True
|
||||||
|
|
||||||
|
def request_failed(request_receipt):
|
||||||
|
nonlocal request_resolved, request_status
|
||||||
|
request_status = "unknown"
|
||||||
|
request_resolved = True
|
||||||
|
|
||||||
|
def fetch_resource_started(resource):
|
||||||
|
nonlocal resource_status
|
||||||
|
current_resource = resource
|
||||||
|
current_resource.progress_callback(sender_progress)
|
||||||
|
resource_status = "started"
|
||||||
|
|
||||||
|
def fetch_resource_concluded(resource):
|
||||||
|
nonlocal resource_resolved, resource_status
|
||||||
|
if resource.status == RNS.Resource.COMPLETE:
|
||||||
|
if resource.total_size > 4:
|
||||||
|
filename_len = int.from_bytes(resource.data.read(2), "big")
|
||||||
|
filename = resource.data.read(filename_len).decode("utf-8")
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
saved_filename = filename
|
||||||
|
while os.path.isfile(saved_filename):
|
||||||
|
counter += 1
|
||||||
|
saved_filename = filename+"."+str(counter)
|
||||||
|
|
||||||
|
file = open(saved_filename, "wb")
|
||||||
|
file.write(resource.data.read())
|
||||||
|
file.close()
|
||||||
|
resource_status = "completed"
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Invalid data received, ignoring resource")
|
||||||
|
resource_status = "invalid_data"
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Resource failed")
|
||||||
|
resource_status = "failed"
|
||||||
|
|
||||||
|
resource_resolved = True
|
||||||
|
|
||||||
|
link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
|
||||||
|
link.set_resource_started_callback(fetch_resource_started)
|
||||||
|
link.set_resource_concluded_callback(fetch_resource_concluded)
|
||||||
|
link.request("fetch_file", data=file, response_callback=request_response, failed_callback=request_failed)
|
||||||
|
|
||||||
|
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||||
|
while not request_resolved:
|
||||||
|
if not silent:
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
if request_status == "not_found":
|
||||||
|
if not silent: print("\r \r", end="")
|
||||||
|
print("Fetch request failed, the file "+str(file)+" was not found on the remote")
|
||||||
|
link.teardown()
|
||||||
|
time.sleep(1)
|
||||||
|
exit(0)
|
||||||
|
elif request_status == "remote_error":
|
||||||
|
if not silent: print("\r \r", end="")
|
||||||
|
print("Fetch request failed due to an error on the remote system")
|
||||||
|
link.teardown()
|
||||||
|
time.sleep(1)
|
||||||
|
exit(0)
|
||||||
|
elif request_status == "unknown":
|
||||||
|
if not silent: print("\r \r", end="")
|
||||||
|
print("Fetch request failed due to an unknown error (probably not authorised)")
|
||||||
|
link.teardown()
|
||||||
|
time.sleep(1)
|
||||||
|
exit(0)
|
||||||
|
elif request_status == "found":
|
||||||
|
if not silent: print("\r \r", end="")
|
||||||
|
|
||||||
|
while not resource_resolved:
|
||||||
|
if not silent:
|
||||||
|
time.sleep(0.1)
|
||||||
|
if current_resource:
|
||||||
|
prg = current_resource.get_progress()
|
||||||
|
percent = round(prg * 100.0, 1)
|
||||||
|
stat_str = str(percent)+"% - " + size_str(int(prg*current_resource.total_size)) + " of " + size_str(current_resource.total_size) + " - " +size_str(speed, "b")+"ps"
|
||||||
|
print("\r \rTransferring file "+syms[i]+" "+stat_str, end=" ")
|
||||||
|
else:
|
||||||
|
print("\r \rWaiting for transfer to start "+syms[i]+" ", end=" ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
if current_resource.status != RNS.Resource.COMPLETE:
|
||||||
|
if silent:
|
||||||
|
print("The transfer failed")
|
||||||
|
else:
|
||||||
|
print("\r \rThe transfer failed")
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
if silent:
|
||||||
|
print(str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \r"+str(file)+" fetched from "+RNS.prettyhexrep(destination_hash))
|
||||||
|
link.teardown()
|
||||||
|
time.sleep(0.25)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
link.teardown()
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def send(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False):
|
||||||
global current_resource, resource_done, link, speed
|
global current_resource, resource_done, link, speed
|
||||||
from tempfile import TemporaryFile
|
from tempfile import TemporaryFile
|
||||||
targetloglevel = 3+verbosity-quietness
|
targetloglevel = 3+verbosity-quietness
|
||||||
@@ -227,6 +521,11 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
|
|||||||
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
||||||
if os.path.isfile(identity_path):
|
if os.path.isfile(identity_path):
|
||||||
identity = RNS.Identity.from_file(identity_path)
|
identity = RNS.Identity.from_file(identity_path)
|
||||||
|
if identity == None:
|
||||||
|
RNS.log("Could not load identity for rncp. The identity file at \""+str(identity_path)+"\" may be corrupt or unreadable.", RNS.LOG_ERROR)
|
||||||
|
exit(2)
|
||||||
|
else:
|
||||||
|
identity = None
|
||||||
|
|
||||||
if identity == None:
|
if identity == None:
|
||||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||||
@@ -235,23 +534,33 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
|
|||||||
|
|
||||||
if not RNS.Transport.has_path(destination_hash):
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
RNS.Transport.request_path(destination_hash)
|
RNS.Transport.request_path(destination_hash)
|
||||||
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
if silent:
|
||||||
|
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested")
|
||||||
|
else:
|
||||||
|
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||||
estab_timeout = time.time()+timeout
|
estab_timeout = time.time()+timeout
|
||||||
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
|
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
|
||||||
time.sleep(0.1)
|
if not silent:
|
||||||
print(("\b\b"+syms[i]+" "), end="")
|
time.sleep(0.1)
|
||||||
sys.stdout.flush()
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
i = (i+1)%len(syms)
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
if not RNS.Transport.has_path(destination_hash):
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
print("\r \rPath not found")
|
if silent:
|
||||||
|
print("Path not found")
|
||||||
|
else:
|
||||||
|
print("\r \rPath not found")
|
||||||
exit(1)
|
exit(1)
|
||||||
else:
|
else:
|
||||||
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
|
if silent:
|
||||||
|
print("Establishing link with "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
|
||||||
|
|
||||||
receiver_identity = RNS.Identity.recall(destination_hash)
|
receiver_identity = RNS.Identity.recall(destination_hash)
|
||||||
receiver_destination = RNS.Destination(
|
receiver_destination = RNS.Destination(
|
||||||
@@ -264,48 +573,75 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
|
|||||||
|
|
||||||
link = RNS.Link(receiver_destination)
|
link = RNS.Link(receiver_destination)
|
||||||
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
|
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
|
||||||
time.sleep(0.1)
|
if not silent:
|
||||||
print(("\b\b"+syms[i]+" "), end="")
|
time.sleep(0.1)
|
||||||
sys.stdout.flush()
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
i = (i+1)%len(syms)
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
if not RNS.Transport.has_path(destination_hash):
|
if time.time() > estab_timeout:
|
||||||
print("\r \rCould not establish link with "+RNS.prettyhexrep(destination_hash))
|
if silent:
|
||||||
|
print("Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out")
|
||||||
|
else:
|
||||||
|
print("\r \rLink establishment with "+RNS.prettyhexrep(destination_hash)+" timed out")
|
||||||
|
exit(1)
|
||||||
|
elif not RNS.Transport.has_path(destination_hash):
|
||||||
|
if silent:
|
||||||
|
print("No path found to "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \rNo path found to "+RNS.prettyhexrep(destination_hash))
|
||||||
exit(1)
|
exit(1)
|
||||||
else:
|
else:
|
||||||
print("\r \rAdvertising file resource ", end=" ")
|
if silent:
|
||||||
|
print("Advertising file resource...")
|
||||||
|
else:
|
||||||
|
print("\r \rAdvertising file resource ", end=" ")
|
||||||
|
|
||||||
link.identify(identity)
|
link.identify(identity)
|
||||||
resource = RNS.Resource(temp_file, link, callback = sender_progress, progress_callback = sender_progress)
|
resource = RNS.Resource(temp_file, link, callback = sender_progress, progress_callback = sender_progress)
|
||||||
current_resource = resource
|
current_resource = resource
|
||||||
|
|
||||||
while resource.status < RNS.Resource.TRANSFERRING:
|
while resource.status < RNS.Resource.TRANSFERRING:
|
||||||
time.sleep(0.1)
|
if not silent:
|
||||||
print(("\b\b"+syms[i]+" "), end="")
|
time.sleep(0.1)
|
||||||
sys.stdout.flush()
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
i = (i+1)%len(syms)
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
|
||||||
if resource.status > RNS.Resource.COMPLETE:
|
if resource.status > RNS.Resource.COMPLETE:
|
||||||
print("\r \rFile was not accepted by "+RNS.prettyhexrep(destination_hash))
|
if silent:
|
||||||
|
print("File was not accepted by "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \rFile was not accepted by "+RNS.prettyhexrep(destination_hash))
|
||||||
exit(1)
|
exit(1)
|
||||||
else:
|
else:
|
||||||
print("\r \rTransferring file ", end=" ")
|
if silent:
|
||||||
|
print("Transferring file...")
|
||||||
|
else:
|
||||||
|
print("\r \rTransferring file ", end=" ")
|
||||||
|
|
||||||
while not resource_done:
|
while not resource_done:
|
||||||
time.sleep(0.1)
|
if not silent:
|
||||||
prg = current_resource.get_progress()
|
time.sleep(0.1)
|
||||||
percent = round(prg * 100.0, 1)
|
prg = current_resource.get_progress()
|
||||||
stat_str = str(percent)+"% - " + size_str(int(prg*current_resource.total_size)) + " of " + size_str(current_resource.total_size) + " - " +size_str(speed, "b")+"ps"
|
percent = round(prg * 100.0, 1)
|
||||||
print("\r \rTransferring file "+syms[i]+" "+stat_str, end=" ")
|
stat_str = str(percent)+"% - " + size_str(int(prg*current_resource.total_size)) + " of " + size_str(current_resource.total_size) + " - " +size_str(speed, "b")+"ps"
|
||||||
sys.stdout.flush()
|
print("\r \rTransferring file "+syms[i]+" "+stat_str, end=" ")
|
||||||
i = (i+1)%len(syms)
|
sys.stdout.flush()
|
||||||
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
if current_resource.status != RNS.Resource.COMPLETE:
|
if current_resource.status != RNS.Resource.COMPLETE:
|
||||||
print("\r \rThe transfer failed")
|
if silent:
|
||||||
|
print("The transfer failed")
|
||||||
|
else:
|
||||||
|
print("\r \rThe transfer failed")
|
||||||
exit(1)
|
exit(1)
|
||||||
else:
|
else:
|
||||||
print("\r \r"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
|
if silent:
|
||||||
|
print(str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("\r \r"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
|
||||||
link.teardown()
|
link.teardown()
|
||||||
time.sleep(0.25)
|
time.sleep(0.25)
|
||||||
real_file.close()
|
real_file.close()
|
||||||
@@ -320,19 +656,21 @@ def main():
|
|||||||
parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0, help="increase verbosity")
|
parser.add_argument('-v', '--verbose', action='count', default=0, help="increase verbosity")
|
||||||
parser.add_argument('-q', '--quiet', action='count', default=0, help="decrease verbosity")
|
parser.add_argument('-q', '--quiet', action='count', default=0, help="decrease verbosity")
|
||||||
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
|
parser.add_argument("-S", '--silent', action='store_true', default=False, help="disable transfer progress output")
|
||||||
parser.add_argument("-r", '--receive', action='store_true', default=False, help="wait for incoming files")
|
parser.add_argument("-l", '--listen', action='store_true', default=False, help="listen for incoming transfer requests")
|
||||||
parser.add_argument("-b", '--no-announce', action='store_true', default=False, help="don't announce at program start")
|
parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending")
|
||||||
|
parser.add_argument("-b", action='store', metavar="seconds", default=-1, help="announce interval, 0 to only announce at startup", type=int)
|
||||||
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="accept from this identity", type=str)
|
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="accept from this identity", type=str)
|
||||||
parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept files from anyone")
|
parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept files from anyone")
|
||||||
|
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
|
||||||
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
||||||
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
|
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
|
||||||
parser.add_argument("--version", action="version", version="rncp {version}".format(version=__version__))
|
parser.add_argument("--version", action="version", version="rncp {version}".format(version=__version__))
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.receive or args.print_identity:
|
if args.listen or args.print_identity:
|
||||||
receive(
|
listen(
|
||||||
configdir = args.config,
|
configdir = args.config,
|
||||||
verbosity=args.verbose,
|
verbosity=args.verbose,
|
||||||
quietness=args.quiet,
|
quietness=args.quiet,
|
||||||
@@ -340,9 +678,25 @@ def main():
|
|||||||
display_identity=args.print_identity,
|
display_identity=args.print_identity,
|
||||||
# limit=args.limit,
|
# limit=args.limit,
|
||||||
disable_auth=args.no_auth,
|
disable_auth=args.no_auth,
|
||||||
disable_announce=args.no_announce,
|
announce=args.b,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
elif args.fetch:
|
||||||
|
if args.destination != None and args.file != None:
|
||||||
|
fetch(
|
||||||
|
configdir = args.config,
|
||||||
|
verbosity = args.verbose,
|
||||||
|
quietness = args.quiet,
|
||||||
|
destination = args.destination,
|
||||||
|
file = args.file,
|
||||||
|
timeout = args.w,
|
||||||
|
silent = args.silent,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("")
|
||||||
|
parser.print_help()
|
||||||
|
print("")
|
||||||
|
|
||||||
elif args.destination != None and args.file != None:
|
elif args.destination != None and args.file != None:
|
||||||
send(
|
send(
|
||||||
configdir = args.config,
|
configdir = args.config,
|
||||||
@@ -351,6 +705,7 @@ def main():
|
|||||||
destination = args.destination,
|
destination = args.destination,
|
||||||
file = args.file,
|
file = args.file,
|
||||||
timeout = args.w,
|
timeout = args.w,
|
||||||
|
silent = args.silent,
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ def main():
|
|||||||
parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
|
parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
|
||||||
|
|
||||||
parser.add_argument("-a", "--announce", metavar="aspects", action="store", default=None, help="announce a destination based on this Identity")
|
parser.add_argument("-a", "--announce", metavar="aspects", action="store", default=None, help="announce a destination based on this Identity")
|
||||||
parser.add_argument("-H", "--hash", metavar="aspects", action="store", default=None, help="show destination hash5s for other aspects for this Identity")
|
parser.add_argument("-H", "--hash", metavar="aspects", action="store", default=None, help="show destination hashes for other aspects for this Identity")
|
||||||
parser.add_argument("-e", "--encrypt", metavar="path", action="store", default=None, help="encrypt file")
|
parser.add_argument("-e", "--encrypt", metavar="path", action="store", default=None, help="encrypt file")
|
||||||
parser.add_argument("-d", "--decrypt", metavar="path", action="store", default=None, help="decrypt file")
|
parser.add_argument("-d", "--decrypt", metavar="path", action="store", default=None, help="decrypt file")
|
||||||
parser.add_argument("-s", "--sign", metavar="path", action="store", default=None, help="sign file")
|
parser.add_argument("-s", "--sign", metavar="path", action="store", default=None, help="sign file")
|
||||||
@@ -166,8 +166,8 @@ def main():
|
|||||||
RNS.log("Identity request timed out", RNS.LOG_ERROR)
|
RNS.log("Identity request timed out", RNS.LOG_ERROR)
|
||||||
exit(6)
|
exit(6)
|
||||||
else:
|
else:
|
||||||
RNS.log("Received Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash)+" from the network")
|
|
||||||
identity = RNS.Identity.recall(destination_hash)
|
identity = RNS.Identity.recall(destination_hash)
|
||||||
|
RNS.log("Received Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash)+" from the network")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Recalled Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash))
|
RNS.log("Recalled Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash))
|
||||||
@@ -196,7 +196,7 @@ def main():
|
|||||||
if args.hash:
|
if args.hash:
|
||||||
try:
|
try:
|
||||||
aspects = args.hash.split(".")
|
aspects = args.hash.split(".")
|
||||||
if not len(aspects) > 1:
|
if not len(aspects) > 0:
|
||||||
RNS.log("Invalid destination aspects specified", RNS.LOG_ERROR)
|
RNS.log("Invalid destination aspects specified", RNS.LOG_ERROR)
|
||||||
exit(32)
|
exit(32)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 Mark Qvist / unsigned.io
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
import RNS
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
from RNS._version import __version__
|
||||||
|
|
||||||
|
|
||||||
|
def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
|
||||||
|
targetverbosity = verbosity-quietness
|
||||||
|
|
||||||
|
if service:
|
||||||
|
targetlogdest = RNS.LOG_FILE
|
||||||
|
targetverbosity = None
|
||||||
|
else:
|
||||||
|
targetlogdest = RNS.LOG_STDOUT
|
||||||
|
|
||||||
|
reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
parser = argparse.ArgumentParser(description="Reticulum Distributed Identity Resolver")
|
||||||
|
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||||
|
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||||
|
parser.add_argument('-q', '--quiet', action='count', default=0)
|
||||||
|
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
||||||
|
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.exampleconfig:
|
||||||
|
print(__example_rns_config__)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
if args.config:
|
||||||
|
configarg = args.config
|
||||||
|
else:
|
||||||
|
configarg = None
|
||||||
|
|
||||||
|
program_setup(configdir = configarg, verbosity=args.verbose, quietness=args.quiet)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
__example_rns_config__ = '''# This is an example Identity Resolver file.
|
||||||
|
'''
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Regular → Executable
+69
-30
@@ -1,4 +1,4 @@
|
|||||||
#!python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
@@ -41,7 +41,7 @@ import RNS
|
|||||||
RNS.logtimefmt = "%H:%M:%S"
|
RNS.logtimefmt = "%H:%M:%S"
|
||||||
RNS.compact_log_fmt = True
|
RNS.compact_log_fmt = True
|
||||||
|
|
||||||
program_version = "2.1.2"
|
program_version = "2.1.3"
|
||||||
eth_addr = "0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a"
|
eth_addr = "0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a"
|
||||||
btc_addr = "3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq"
|
btc_addr = "3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq"
|
||||||
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
|
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
|
||||||
@@ -80,6 +80,7 @@ class KISS():
|
|||||||
CMD_BLINK = 0x30
|
CMD_BLINK = 0x30
|
||||||
CMD_RANDOM = 0x40
|
CMD_RANDOM = 0x40
|
||||||
CMD_DISP_INT = 0x45
|
CMD_DISP_INT = 0x45
|
||||||
|
CMD_DISP_ADR = 0x63
|
||||||
CMD_BT_CTRL = 0x46
|
CMD_BT_CTRL = 0x46
|
||||||
CMD_BT_PIN = 0x62
|
CMD_BT_PIN = 0x62
|
||||||
CMD_BOARD = 0x47
|
CMD_BOARD = 0x47
|
||||||
@@ -237,6 +238,7 @@ try:
|
|||||||
EXT_DIR = CNF_DIR+"/extracted"
|
EXT_DIR = CNF_DIR+"/extracted"
|
||||||
RT_PATH = CNF_DIR+"/recovery_esptool.py"
|
RT_PATH = CNF_DIR+"/recovery_esptool.py"
|
||||||
TK_DIR = CNF_DIR+"/trusted_keys"
|
TK_DIR = CNF_DIR+"/trusted_keys"
|
||||||
|
ROM_DIR = CNF_DIR+"/eeprom"
|
||||||
|
|
||||||
if not os.path.isdir(CNF_DIR):
|
if not os.path.isdir(CNF_DIR):
|
||||||
os.makedirs(CNF_DIR)
|
os.makedirs(CNF_DIR)
|
||||||
@@ -248,6 +250,8 @@ try:
|
|||||||
os.makedirs(EXT_DIR)
|
os.makedirs(EXT_DIR)
|
||||||
if not os.path.isdir(TK_DIR):
|
if not os.path.isdir(TK_DIR):
|
||||||
os.makedirs(TK_DIR)
|
os.makedirs(TK_DIR)
|
||||||
|
if not os.path.isdir(ROM_DIR):
|
||||||
|
os.makedirs(ROM_DIR)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("No access to directory "+str(CNF_DIR)+". This utility needs file system access to store firmware and data files. Cannot continue.")
|
print("No access to directory "+str(CNF_DIR)+". This utility needs file system access to store firmware and data files. Cannot continue.")
|
||||||
@@ -574,7 +578,14 @@ class RNode():
|
|||||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_INT])+data+bytes([KISS.FEND])
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_INT])+data+bytes([KISS.FEND])
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise IOError("An IO error occurred while sending bluetooth enable command to device")
|
raise IOError("An IO error occurred while sending display intensity command to device")
|
||||||
|
|
||||||
|
def set_display_address(self, address):
|
||||||
|
data = bytes([address & 0xFF])
|
||||||
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_ADR])+data+bytes([KISS.FEND])
|
||||||
|
written = self.serial.write(kiss_command)
|
||||||
|
if written != len(kiss_command):
|
||||||
|
raise IOError("An IO error occurred while sending display address command to device")
|
||||||
|
|
||||||
def enable_bluetooth(self):
|
def enable_bluetooth(self):
|
||||||
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
|
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
|
||||||
@@ -1131,6 +1142,7 @@ def main():
|
|||||||
parser.add_argument("-e", "--extract", action="store_true", help="Extract firmware from connected RNode for later use")
|
parser.add_argument("-e", "--extract", action="store_true", help="Extract firmware from connected RNode for later use")
|
||||||
parser.add_argument("-E", "--use-extracted", action="store_true", help="Use the extracted firmware for autoinstallation or update")
|
parser.add_argument("-E", "--use-extracted", action="store_true", help="Use the extracted firmware for autoinstallation or update")
|
||||||
parser.add_argument("-C", "--clear-cache", action="store_true", help="Clear locally cached firmware files")
|
parser.add_argument("-C", "--clear-cache", action="store_true", help="Clear locally cached firmware files")
|
||||||
|
parser.add_argument("--baud-flash", action="store", metavar="baud_flash", type=str, default="921600", help="Set specific baud rate when flashing device. Default is 921600")
|
||||||
|
|
||||||
parser.add_argument("-N", "--normal", action="store_true", help="Switch device to normal mode")
|
parser.add_argument("-N", "--normal", action="store_true", help="Switch device to normal mode")
|
||||||
parser.add_argument("-T", "--tnc", action="store_true", help="Switch device to TNC mode")
|
parser.add_argument("-T", "--tnc", action="store_true", help="Switch device to TNC mode")
|
||||||
@@ -1140,6 +1152,7 @@ def main():
|
|||||||
parser.add_argument("-p", "--bluetooth-pair", action="store_true", help="Put device into bluetooth pairing mode")
|
parser.add_argument("-p", "--bluetooth-pair", action="store_true", help="Put device into bluetooth pairing mode")
|
||||||
|
|
||||||
parser.add_argument("-D", "--display", action="store", metavar="i", type=int, default=None, help="Set display intensity (0-255)")
|
parser.add_argument("-D", "--display", action="store", metavar="i", type=int, default=None, help="Set display intensity (0-255)")
|
||||||
|
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("--freq", action="store", metavar="Hz", type=int, default=None, help="Frequency in Hz for TNC mode")
|
parser.add_argument("--freq", action="store", metavar="Hz", type=int, default=None, help="Frequency in Hz for TNC mode")
|
||||||
parser.add_argument("--bw", action="store", metavar="Hz", type=int, default=None, help="Bandwidth in Hz for TNC mode")
|
parser.add_argument("--bw", action="store", metavar="Hz", type=int, default=None, help="Bandwidth in Hz for TNC mode")
|
||||||
@@ -1348,11 +1361,11 @@ def main():
|
|||||||
hash_f.close()
|
hash_f.close()
|
||||||
|
|
||||||
extraction_parts = [
|
extraction_parts = [
|
||||||
("bootloader", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud 921600 --before default_reset --after hard_reset read_flash 0x1000 0x4650 \""+EXT_DIR+"/extracted_rnode_firmware.bootloader\""),
|
("bootloader", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x1000 0x4650 \""+EXT_DIR+"/extracted_rnode_firmware.bootloader\""),
|
||||||
("partition table", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud 921600 --before default_reset --after hard_reset read_flash 0x8000 0xC00 \""+EXT_DIR+"/extracted_rnode_firmware.partitions\""),
|
("partition table", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x8000 0xC00 \""+EXT_DIR+"/extracted_rnode_firmware.partitions\""),
|
||||||
("app boot", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud 921600 --before default_reset --after hard_reset read_flash 0xe000 0x2000 \""+EXT_DIR+"/extracted_rnode_firmware.boot_app0\""),
|
("app boot", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0xe000 0x2000 \""+EXT_DIR+"/extracted_rnode_firmware.boot_app0\""),
|
||||||
("application image", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud 921600 --before default_reset --after hard_reset read_flash 0x10000 0x200000 \""+EXT_DIR+"/extracted_rnode_firmware.bin\""),
|
("application image", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x10000 0x200000 \""+EXT_DIR+"/extracted_rnode_firmware.bin\""),
|
||||||
("console image", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud 921600 --before default_reset --after hard_reset read_flash 0x210000 0x1F0000 \""+EXT_DIR+"/extracted_console_image.bin\""),
|
("console image", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x210000 0x1F0000 \""+EXT_DIR+"/extracted_console_image.bin\""),
|
||||||
]
|
]
|
||||||
import subprocess, shlex
|
import subprocess, shlex
|
||||||
for part, command in extraction_parts:
|
for part, command in extraction_parts:
|
||||||
@@ -1494,7 +1507,7 @@ def main():
|
|||||||
selected_product = None
|
selected_product = None
|
||||||
try:
|
try:
|
||||||
c_dev = int(input())
|
c_dev = int(input())
|
||||||
if c_dev < 1 or c_dev > 6:
|
if c_dev < 1 or c_dev > 7:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
elif c_dev == 1:
|
elif c_dev == 1:
|
||||||
selected_product = ROM.PRODUCT_RNODE
|
selected_product = ROM.PRODUCT_RNODE
|
||||||
@@ -2055,7 +2068,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2073,7 +2086,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2091,7 +2104,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2109,7 +2122,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2127,7 +2140,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2145,7 +2158,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2163,7 +2176,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2181,7 +2194,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2199,7 +2212,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2217,7 +2230,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2235,7 +2248,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2253,7 +2266,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2271,7 +2284,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2289,7 +2302,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2307,7 +2320,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2325,7 +2338,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2343,7 +2356,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2361,7 +2374,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2378,7 +2391,7 @@ def main():
|
|||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
"--chip", "esp32",
|
"--chip", "esp32",
|
||||||
"--port", args.port,
|
"--port", args.port,
|
||||||
"--baud", "921600",
|
"--baud", args.baud_flash,
|
||||||
"--before", "default_reset",
|
"--before", "default_reset",
|
||||||
"--after", "hard_reset",
|
"--after", "hard_reset",
|
||||||
"write_flash", "-z",
|
"write_flash", "-z",
|
||||||
@@ -2466,6 +2479,10 @@ def main():
|
|||||||
RNS.log("Waiting for ESP32 reset...")
|
RNS.log("Waiting for ESP32 reset...")
|
||||||
time.sleep(7)
|
time.sleep(7)
|
||||||
else:
|
else:
|
||||||
|
RNS.log("Error from flasher ("+str(flash_status)+") while writing.")
|
||||||
|
RNS.log("Some boards have trouble flashing at high speeds, and you can")
|
||||||
|
RNS.log("try flashing with a lower baud rate, as in this example:")
|
||||||
|
RNS.log("rnodeconf --autoinstall --baud-flash 115200")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -2604,7 +2621,11 @@ def main():
|
|||||||
vf.close()
|
vf.close()
|
||||||
else:
|
else:
|
||||||
partition_filename = fw_filename.replace(".zip", ".bin")
|
partition_filename = fw_filename.replace(".zip", ".bin")
|
||||||
partition_hash = get_partition_hash(UPD_DIR+"/"+selected_version+"/"+partition_filename)
|
if fw_filename == "extracted_rnode_firmware.zip":
|
||||||
|
partition_full_path = EXT_DIR+"/extracted_rnode_firmware.bin"
|
||||||
|
else:
|
||||||
|
partition_full_path = UPD_DIR+"/"+selected_version+"/"+partition_filename
|
||||||
|
partition_hash = get_partition_hash(partition_full_path)
|
||||||
if partition_hash != None:
|
if partition_hash != None:
|
||||||
rnode.set_firmware_hash(partition_hash)
|
rnode.set_firmware_hash(partition_hash)
|
||||||
rnode.indicate_firmware_update()
|
rnode.indicate_firmware_update()
|
||||||
@@ -2673,7 +2694,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
timestamp = time.time()
|
timestamp = time.time()
|
||||||
filename = str(time.strftime("%Y-%m-%d_%H-%M-%S"))
|
filename = str(time.strftime("%Y-%m-%d_%H-%M-%S"))
|
||||||
path = "./eeprom/"+filename+".eeprom"
|
path = ROM_DIR + filename + ".eeprom"
|
||||||
file = open(path, "wb")
|
file = open(path, "wb")
|
||||||
file.write(rnode.eeprom)
|
file.write(rnode.eeprom)
|
||||||
file.close()
|
file.close()
|
||||||
@@ -2692,6 +2713,24 @@ def main():
|
|||||||
RNS.log("Setting display intensity to "+str(di))
|
RNS.log("Setting display intensity to "+str(di))
|
||||||
rnode.set_display_intensity(di)
|
rnode.set_display_intensity(di)
|
||||||
|
|
||||||
|
if isinstance(args.display_addr, str):
|
||||||
|
set_addr = False
|
||||||
|
try:
|
||||||
|
if args.display_addr.startswith("0x"):
|
||||||
|
args.display_addr = args.display_addr[2:]
|
||||||
|
da = bytes.fromhex(args.display_addr)
|
||||||
|
set_addr = True
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if set_addr and len(da) == 1:
|
||||||
|
RNS.log("Setting display address to "+RNS.hexrep(da, delimit=False))
|
||||||
|
rnode.set_display_address(ord(da))
|
||||||
|
rnode.hard_reset()
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
RNS.log("Invalid display address specified")
|
||||||
|
|
||||||
if args.bluetooth_on:
|
if args.bluetooth_on:
|
||||||
RNS.log("Enabling Bluetooth...")
|
RNS.log("Enabling Bluetooth...")
|
||||||
rnode.enable_bluetooth()
|
rnode.enable_bluetooth()
|
||||||
|
|||||||
+45
-9
@@ -30,7 +30,7 @@ import argparse
|
|||||||
from RNS._version import __version__
|
from RNS._version import __version__
|
||||||
|
|
||||||
|
|
||||||
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues):
|
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues, drop_via):
|
||||||
if table:
|
if table:
|
||||||
destination_hash = None
|
destination_hash = None
|
||||||
if destination_hexhash != None:
|
if destination_hexhash != None:
|
||||||
@@ -155,6 +155,29 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
elif drop_via:
|
||||||
|
try:
|
||||||
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
|
if len(destination_hexhash) != dest_len:
|
||||||
|
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||||
|
try:
|
||||||
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError("Invalid destination entered. Check your input.")
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||||
|
|
||||||
|
if reticulum.drop_all_via(destination_hash):
|
||||||
|
print("Dropped all paths via "+RNS.prettyhexrep(destination_hash))
|
||||||
|
else:
|
||||||
|
print("Unable to drop paths via "+RNS.prettyhexrep(destination_hash)+". Does the transport instance exist?")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
@@ -187,15 +210,20 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
|||||||
|
|
||||||
if RNS.Transport.has_path(destination_hash):
|
if RNS.Transport.has_path(destination_hash):
|
||||||
hops = RNS.Transport.hops_to(destination_hash)
|
hops = RNS.Transport.hops_to(destination_hash)
|
||||||
next_hop = RNS.prettyhexrep(reticulum.get_next_hop(destination_hash))
|
next_hop_bytes = reticulum.get_next_hop(destination_hash)
|
||||||
next_hop_interface = reticulum.get_next_hop_if_name(destination_hash)
|
if next_hop_bytes == None:
|
||||||
|
print("\r \rError: Invalid path data returned")
|
||||||
if hops != 1:
|
sys.exit(1)
|
||||||
ms = "s"
|
|
||||||
else:
|
else:
|
||||||
ms = ""
|
next_hop = RNS.prettyhexrep(next_hop_bytes)
|
||||||
|
next_hop_interface = reticulum.get_next_hop_if_name(destination_hash)
|
||||||
|
|
||||||
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface)
|
if hops != 1:
|
||||||
|
ms = "s"
|
||||||
|
else:
|
||||||
|
ms = ""
|
||||||
|
|
||||||
|
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface)
|
||||||
else:
|
else:
|
||||||
print("\r \rPath not found")
|
print("\r \rPath not found")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -251,6 +279,13 @@ def main():
|
|||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-x", "--drop-via",
|
||||||
|
action="store_true",
|
||||||
|
help="drop all paths via specified transport instance",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-w",
|
"-w",
|
||||||
action="store",
|
action="store",
|
||||||
@@ -277,7 +312,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
configarg = None
|
configarg = None
|
||||||
|
|
||||||
if not args.drop_announces and not args.table and not args.rates and not args.destination:
|
if not args.drop_announces and not args.table and not args.rates and not args.destination and not args.drop_via:
|
||||||
print("")
|
print("")
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
print("")
|
print("")
|
||||||
@@ -291,6 +326,7 @@ def main():
|
|||||||
verbosity = args.verbose,
|
verbosity = args.verbose,
|
||||||
timeout = args.w,
|
timeout = args.w,
|
||||||
drop_queues = args.drop_announces,
|
drop_queues = args.drop_announces,
|
||||||
|
drop_via = args.drop_via,
|
||||||
)
|
)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|||||||
+72
-67
@@ -31,8 +31,10 @@ import argparse
|
|||||||
from RNS._version import __version__
|
from RNS._version import __version__
|
||||||
|
|
||||||
DEFAULT_PROBE_SIZE = 16
|
DEFAULT_PROBE_SIZE = 16
|
||||||
|
DEFAULT_TIMEOUT = 15
|
||||||
|
|
||||||
def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_name = None, verbosity = 0):
|
def program_setup(configdir, destination_hexhash, size=None, full_name = None, verbosity = 0, timeout=None):
|
||||||
|
if size == None: size = DEFAULT_PROBE_SIZE
|
||||||
if full_name == None:
|
if full_name == None:
|
||||||
print("The full destination name including application name aspects must be specified for the destination")
|
print("The full destination name including application name aspects must be specified for the destination")
|
||||||
exit()
|
exit()
|
||||||
@@ -71,14 +73,19 @@ def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_
|
|||||||
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT)
|
||||||
i = 0
|
i = 0
|
||||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||||
while not RNS.Transport.has_path(destination_hash):
|
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
print(("\b\b"+syms[i]+" "), end="")
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
i = (i+1)%len(syms)
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
if time.time() > _timeout:
|
||||||
|
print("\r \rPath request timed out")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
server_identity = RNS.Identity.recall(destination_hash)
|
server_identity = RNS.Identity.recall(destination_hash)
|
||||||
|
|
||||||
request_destination = RNS.Destination(
|
request_destination = RNS.Destination(
|
||||||
@@ -89,66 +96,85 @@ def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_
|
|||||||
*aspects
|
*aspects
|
||||||
)
|
)
|
||||||
|
|
||||||
probe = RNS.Packet(request_destination, os.urandom(size))
|
try:
|
||||||
|
probe = RNS.Packet(request_destination, os.urandom(size))
|
||||||
|
probe.pack()
|
||||||
|
except OSError:
|
||||||
|
print("Error: Probe packet size of "+str(len(probe.raw))+" bytes exceed MTU of "+str(RNS.Reticulum.MTU)+" bytes")
|
||||||
|
exit(3)
|
||||||
|
|
||||||
receipt = probe.send()
|
receipt = probe.send()
|
||||||
|
|
||||||
if more_output:
|
if more_output:
|
||||||
more = " via "+RNS.prettyhexrep(reticulum.get_next_hop(destination_hash))+" on "+str(reticulum.get_next_hop_if_name(destination_hash))
|
nhd = reticulum.get_next_hop(destination_hash)
|
||||||
|
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else ""
|
||||||
|
if_str = " on "+str(reticulum.get_next_hop_if_name(destination_hash)) if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
|
||||||
|
more = via_str+if_str
|
||||||
else:
|
else:
|
||||||
more = ""
|
more = ""
|
||||||
|
|
||||||
print("\rSent "+str(size)+" byte probe to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
|
print("\rSent "+str(size)+" byte probe to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
|
||||||
|
|
||||||
|
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT)
|
||||||
i = 0
|
i = 0
|
||||||
while not receipt.status == RNS.PacketReceipt.DELIVERED:
|
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
print(("\b\b"+syms[i]+" "), end="")
|
print(("\b\b"+syms[i]+" "), end="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
i = (i+1)%len(syms)
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
if time.time() > _timeout:
|
||||||
|
print("\r \rProbe timed out")
|
||||||
|
exit(2)
|
||||||
|
|
||||||
print("\b\b ")
|
print("\b\b ")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
hops = RNS.Transport.hops_to(destination_hash)
|
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||||
if hops != 1:
|
hops = RNS.Transport.hops_to(destination_hash)
|
||||||
ms = "s"
|
if hops != 1:
|
||||||
else:
|
ms = "s"
|
||||||
ms = ""
|
else:
|
||||||
|
ms = ""
|
||||||
|
|
||||||
rtt = receipt.get_rtt()
|
rtt = receipt.get_rtt()
|
||||||
if (rtt >= 1):
|
if (rtt >= 1):
|
||||||
rtt = round(rtt, 3)
|
rtt = round(rtt, 3)
|
||||||
rttstring = str(rtt)+" seconds"
|
rttstring = str(rtt)+" seconds"
|
||||||
else:
|
else:
|
||||||
rtt = round(rtt*1000, 3)
|
rtt = round(rtt*1000, 3)
|
||||||
rttstring = str(rtt)+" milliseconds"
|
rttstring = str(rtt)+" milliseconds"
|
||||||
|
|
||||||
reception_stats = ""
|
reception_stats = ""
|
||||||
if reticulum.is_connected_to_shared_instance:
|
if reticulum.is_connected_to_shared_instance:
|
||||||
reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash)
|
reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash)
|
||||||
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
|
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
|
||||||
|
|
||||||
if reception_rssi != None:
|
if reception_rssi != None:
|
||||||
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
|
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
|
||||||
|
|
||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
||||||
|
|
||||||
|
else:
|
||||||
|
if receipt.proof_packet != None:
|
||||||
|
if receipt.proof_packet.rssi != None:
|
||||||
|
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
|
||||||
|
|
||||||
|
if receipt.proof_packet.snr != None:
|
||||||
|
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Valid reply received from "+
|
||||||
|
RNS.prettyhexrep(receipt.destination.hash)+
|
||||||
|
"\nRound-trip time is "+rttstring+
|
||||||
|
" over "+str(hops)+" hop"+ms+
|
||||||
|
reception_stats
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if receipt.proof_packet != None:
|
print("\r \rProbe timed out")
|
||||||
if receipt.proof_packet.rssi != None:
|
exit(2)
|
||||||
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
|
|
||||||
|
|
||||||
if receipt.proof_packet.snr != None:
|
|
||||||
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
|
||||||
|
|
||||||
print(
|
|
||||||
"Valid reply received from "+
|
|
||||||
RNS.prettyhexrep(receipt.destination.hash)+
|
|
||||||
"\nRound-trip time is "+rttstring+
|
|
||||||
" over "+str(hops)+" hop"+ms+
|
|
||||||
reception_stats
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -156,34 +182,12 @@ def main():
|
|||||||
try:
|
try:
|
||||||
parser = argparse.ArgumentParser(description="Reticulum Probe Utility")
|
parser = argparse.ArgumentParser(description="Reticulum Probe Utility")
|
||||||
|
|
||||||
parser.add_argument("--config",
|
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||||
action="store",
|
parser.add_argument("-s", "--size", action="store", default=None, help="size of probe packet payload in bytes", type=int)
|
||||||
default=None,
|
parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float)
|
||||||
help="path to alternative Reticulum config directory",
|
parser.add_argument("--version", action="version", version="rnprobe {version}".format(version=__version__))
|
||||||
type=str
|
parser.add_argument("full_name", nargs="?", default=None, help="full destination name in dotted notation", type=str)
|
||||||
)
|
parser.add_argument("destination_hash", nargs="?", default=None, help="hexadecimal hash of the destination", type=str)
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--version",
|
|
||||||
action="version",
|
|
||||||
version="rnprobe {version}".format(version=__version__)
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"full_name",
|
|
||||||
nargs="?",
|
|
||||||
default=None,
|
|
||||||
help="full destination name in dotted notation",
|
|
||||||
type=str
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"destination_hash",
|
|
||||||
nargs="?",
|
|
||||||
default=None,
|
|
||||||
help="hexadecimal hash of the destination",
|
|
||||||
type=str
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||||
|
|
||||||
@@ -202,6 +206,7 @@ def main():
|
|||||||
program_setup(
|
program_setup(
|
||||||
configdir = configarg,
|
configdir = configarg,
|
||||||
destination_hexhash = args.destination_hash,
|
destination_hexhash = args.destination_hash,
|
||||||
|
size = args.size,
|
||||||
full_name = args.full_name,
|
full_name = args.full_name,
|
||||||
verbosity = args.verbose
|
verbosity = args.verbose
|
||||||
)
|
)
|
||||||
|
|||||||
+24
-1
@@ -87,7 +87,7 @@ __example_rns_config__ = '''# This is an example Reticulum config file.
|
|||||||
# always-on. This directive is optional and can be removed
|
# always-on. This directive is optional and can be removed
|
||||||
# for brevity.
|
# for brevity.
|
||||||
|
|
||||||
enable_transport = False
|
enable_transport = No
|
||||||
|
|
||||||
|
|
||||||
# By default, the first program to launch the Reticulum
|
# By default, the first program to launch the Reticulum
|
||||||
@@ -111,6 +111,18 @@ share_instance = Yes
|
|||||||
shared_instance_port = 37428
|
shared_instance_port = 37428
|
||||||
instance_control_port = 37429
|
instance_control_port = 37429
|
||||||
|
|
||||||
|
|
||||||
|
# On systems where running instances may not have access
|
||||||
|
# to the same shared Reticulum configuration directory,
|
||||||
|
# it is still possible to allow full interactivity for
|
||||||
|
# running instances, by manually specifying a shared RPC
|
||||||
|
# key. In almost all cases, this option is not needed, but
|
||||||
|
# it can be useful on operating systems such as Android.
|
||||||
|
# The key must be specified as bytes in hexadecimal.
|
||||||
|
|
||||||
|
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
|
||||||
|
|
||||||
|
|
||||||
# You can configure Reticulum to panic and forcibly close
|
# You can configure Reticulum to panic and forcibly close
|
||||||
# if an unrecoverable interface error occurs, such as the
|
# if an unrecoverable interface error occurs, such as the
|
||||||
# hardware device for an interface disappearing. This is
|
# hardware device for an interface disappearing. This is
|
||||||
@@ -120,6 +132,17 @@ instance_control_port = 37429
|
|||||||
panic_on_interface_error = No
|
panic_on_interface_error = No
|
||||||
|
|
||||||
|
|
||||||
|
# When Transport is enabled, it is possible to allow the
|
||||||
|
# Transport Instance to respond to probe requests from
|
||||||
|
# the rnprobe utility. This can be a useful tool to test
|
||||||
|
# connectivity. When this option is enabled, the probe
|
||||||
|
# destination will be generated from the Identity of the
|
||||||
|
# Transport Instance, and printed to the log at startup.
|
||||||
|
# Optional, and disabled by default.
|
||||||
|
|
||||||
|
respond_to_probes = No
|
||||||
|
|
||||||
|
|
||||||
[logging]
|
[logging]
|
||||||
# Valid log levels are 0 through 7:
|
# Valid log levels are 0 through 7:
|
||||||
# 0: Log only critical information
|
# 0: Log only critical information
|
||||||
|
|||||||
+100
-24
@@ -46,7 +46,7 @@ def size_str(num, suffix='B'):
|
|||||||
|
|
||||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=False):
|
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, sorting=None, sort_reverse=False):
|
||||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||||
|
|
||||||
stats = None
|
stats = None
|
||||||
@@ -62,16 +62,38 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
|||||||
if isinstance(stats[s], bytes):
|
if isinstance(stats[s], bytes):
|
||||||
stats[s] = RNS.hexrep(stats[s], delimit=False)
|
stats[s] = RNS.hexrep(stats[s], delimit=False)
|
||||||
|
|
||||||
for i in stats[s]:
|
if isinstance(stats[s], dict):
|
||||||
if isinstance(i, dict):
|
for i in stats[s]:
|
||||||
for k in i:
|
if isinstance(i, dict):
|
||||||
if isinstance(i[k], bytes):
|
for k in i:
|
||||||
i[k] = RNS.hexrep(i[k], delimit=False)
|
if isinstance(i[k], bytes):
|
||||||
|
i[k] = RNS.hexrep(i[k], delimit=False)
|
||||||
|
|
||||||
print(json.dumps(stats))
|
print(json.dumps(stats))
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
for ifstat in stats["interfaces"]:
|
interfaces = stats["interfaces"]
|
||||||
|
if sorting != None and isinstance(sorting, str):
|
||||||
|
sorting = sorting.lower()
|
||||||
|
if sorting == "rate" or sorting == "bitrate":
|
||||||
|
interfaces.sort(key=lambda i: i["bitrate"], reverse=not sort_reverse)
|
||||||
|
if sorting == "rx":
|
||||||
|
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 == "traffic":
|
||||||
|
interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse)
|
||||||
|
if sorting == "announces" or sorting == "announce":
|
||||||
|
interfaces.sort(key=lambda i: i["incoming_announce_frequency"]+i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
||||||
|
if sorting == "arx":
|
||||||
|
interfaces.sort(key=lambda i: i["incoming_announce_frequency"], reverse=not sort_reverse)
|
||||||
|
if sorting == "atx":
|
||||||
|
interfaces.sort(key=lambda i: i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
||||||
|
if sorting == "held":
|
||||||
|
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
|
||||||
|
|
||||||
|
|
||||||
|
for ifstat in interfaces:
|
||||||
name = ifstat["name"]
|
name = ifstat["name"]
|
||||||
|
|
||||||
if dispall or not (
|
if dispall or not (
|
||||||
@@ -113,7 +135,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
|||||||
else:
|
else:
|
||||||
spec_str = " programs"
|
spec_str = " programs"
|
||||||
|
|
||||||
clients_string = "Serving : "+str(cnum)+spec_str
|
clients_string = "Serving : "+str(cnum)+spec_str
|
||||||
elif name.startswith("I2PInterface["):
|
elif name.startswith("I2PInterface["):
|
||||||
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
|
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
|
||||||
cnum = clients
|
cnum = clients
|
||||||
@@ -122,11 +144,11 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
|||||||
else:
|
else:
|
||||||
spec_str = " connected I2P endpoints"
|
spec_str = " connected I2P endpoints"
|
||||||
|
|
||||||
clients_string = "Peers : "+str(cnum)+spec_str
|
clients_string = "Peers : "+str(cnum)+spec_str
|
||||||
else:
|
else:
|
||||||
clients_string = ""
|
clients_string = ""
|
||||||
else:
|
else:
|
||||||
clients_string = "Clients : "+str(clients)
|
clients_string = "Clients : "+str(clients)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
clients = None
|
clients = None
|
||||||
@@ -134,43 +156,63 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
|||||||
print(" {n}".format(n=ifstat["name"]))
|
print(" {n}".format(n=ifstat["name"]))
|
||||||
|
|
||||||
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
||||||
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
||||||
|
|
||||||
print(" Status : {ss}".format(ss=ss))
|
print(" Status : {ss}".format(ss=ss))
|
||||||
|
|
||||||
if clients != None and clients_string != "":
|
if clients != None and clients_string != "":
|
||||||
print(" "+clients_string)
|
print(" "+clients_string)
|
||||||
|
|
||||||
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
||||||
print(" Mode : {mode}".format(mode=modestr))
|
print(" Mode : {mode}".format(mode=modestr))
|
||||||
|
|
||||||
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
||||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||||
|
|
||||||
|
if "airtime_short" in ifstat and "airtime_long" in ifstat:
|
||||||
|
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"])))
|
||||||
|
|
||||||
if "peers" in ifstat and ifstat["peers"] != None:
|
if "peers" in ifstat and ifstat["peers"] != None:
|
||||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||||
|
|
||||||
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
|
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
|
||||||
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
|
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
|
||||||
|
|
||||||
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
||||||
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
||||||
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
||||||
|
|
||||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||||
|
|
||||||
if "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
if astats and "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
||||||
aqn = ifstat["announce_queue"]
|
aqn = ifstat["announce_queue"]
|
||||||
if aqn == 1:
|
if aqn == 1:
|
||||||
print(" Queued : {np} announce".format(np=aqn))
|
print(" Queued : {np} announce".format(np=aqn))
|
||||||
else:
|
else:
|
||||||
print(" Queued : {np} announces".format(np=aqn))
|
print(" Queued : {np} announces".format(np=aqn))
|
||||||
|
|
||||||
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
|
||||||
|
aqn = ifstat["held_announces"]
|
||||||
|
if aqn == 1:
|
||||||
|
print(" Held : {np} announce".format(np=aqn))
|
||||||
|
else:
|
||||||
|
print(" Held : {np} announces".format(np=aqn))
|
||||||
|
|
||||||
|
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
|
||||||
|
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"])))
|
||||||
|
|
||||||
if "transport_id" in stats and stats["transport_id"] != None:
|
if "transport_id" in stats and stats["transport_id"] != None:
|
||||||
print("\n Reticulum Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" is running")
|
print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
|
||||||
|
if "probe_responder" in stats and stats["probe_responder"] != None:
|
||||||
|
print(" Probe responder at "+RNS.prettyhexrep(stats["probe_responder"])+ " active")
|
||||||
|
print(" Uptime is "+RNS.prettytime(stats["transport_uptime"]))
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
@@ -191,6 +233,31 @@ def main():
|
|||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-A",
|
||||||
|
"--announce-stats",
|
||||||
|
action="store_true",
|
||||||
|
help="show announce stats",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--sort",
|
||||||
|
action="store",
|
||||||
|
help="sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]",
|
||||||
|
default=None,
|
||||||
|
type=str
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--reverse",
|
||||||
|
action="store_true",
|
||||||
|
help="reverse sorting",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-j",
|
"-j",
|
||||||
"--json",
|
"--json",
|
||||||
@@ -210,7 +277,16 @@ def main():
|
|||||||
else:
|
else:
|
||||||
configarg = None
|
configarg = None
|
||||||
|
|
||||||
program_setup(configdir = configarg, dispall = args.all, verbosity=args.verbose, name_filter=args.filter, json=args.json)
|
program_setup(
|
||||||
|
configdir = configarg,
|
||||||
|
dispall = args.all,
|
||||||
|
verbosity=args.verbose,
|
||||||
|
name_filter=args.filter,
|
||||||
|
json=args.json,
|
||||||
|
astats=args.announce_stats,
|
||||||
|
sorting=args.sort,
|
||||||
|
sort_reverse=args.reverse,
|
||||||
|
)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
|
|||||||
+15
-2
@@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -38,6 +38,7 @@ from .Transport import Transport
|
|||||||
from .Destination import Destination
|
from .Destination import Destination
|
||||||
from .Packet import Packet
|
from .Packet import Packet
|
||||||
from .Packet import PacketReceipt
|
from .Packet import PacketReceipt
|
||||||
|
from .Resolver import Resolver
|
||||||
from .Resource import Resource, ResourceAdvertisement
|
from .Resource import Resource, ResourceAdvertisement
|
||||||
from .Cryptography import HKDF
|
from .Cryptography import HKDF
|
||||||
from .Cryptography import Hashes
|
from .Cryptography import Hashes
|
||||||
@@ -105,7 +106,7 @@ def timestamp_str(time_s):
|
|||||||
|
|
||||||
def log(msg, level=3, _override_destination = False):
|
def log(msg, level=3, _override_destination = False):
|
||||||
global _always_override_destination, compact_log_fmt
|
global _always_override_destination, compact_log_fmt
|
||||||
|
msg = str(msg)
|
||||||
if loglevel >= level:
|
if loglevel >= level:
|
||||||
if not compact_log_fmt:
|
if not compact_log_fmt:
|
||||||
logstring = "["+timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg
|
logstring = "["+timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg
|
||||||
@@ -179,6 +180,18 @@ def prettysize(num, suffix='B'):
|
|||||||
|
|
||||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
|
def prettyfrequency(hz, suffix="Hz"):
|
||||||
|
num = hz*1e6
|
||||||
|
units = ["µ", "m", "", "K","M","G","T","P","E","Z"]
|
||||||
|
last_unit = "Y"
|
||||||
|
|
||||||
|
for unit in units:
|
||||||
|
if abs(num) < 1000.0:
|
||||||
|
return "%.2f %s%s" % (num, unit, suffix)
|
||||||
|
num /= 1000.0
|
||||||
|
|
||||||
|
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
def prettytime(time, verbose=False):
|
def prettytime(time, verbose=False):
|
||||||
days = int(time // (24 * 3600))
|
days = int(time // (24 * 3600))
|
||||||
time = time % (24 * 3600)
|
time = time % (24 * 3600)
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
__version__ = "0.5.2"
|
__version__ = "0.6.2"
|
||||||
|
|||||||
+10
-9
@@ -14,12 +14,14 @@ This document outlines the currently established development roadmap for Reticul
|
|||||||
## Currently Active Work Areas
|
## Currently Active Work Areas
|
||||||
For each release cycle of Reticulum, improvements and additions from the five [Primary Efforts](#primary-efforts) are selected as active work areas, and can be expected to be included in the upcoming releases within that cycle. While not entirely set in stone for each release cycle, they serve as a pointer of what to expect in the near future.
|
For each release cycle of Reticulum, improvements and additions from the five [Primary Efforts](#primary-efforts) are selected as active work areas, and can be expected to be included in the upcoming releases within that cycle. While not entirely set in stone for each release cycle, they serve as a pointer of what to expect in the near future.
|
||||||
|
|
||||||
- The current `0.5.x` release cycle aims at completing
|
- The current `0.6.x` release cycle aims at completing
|
||||||
- [ ] Overhauling and updating the documentation
|
- [ ] Overhauling and updating the documentation
|
||||||
- [ ] Performance and memory optimisations of the Python reference implementation
|
- [ ] Distributed Destination Naming System
|
||||||
- [ ] Fixing potential bugs
|
- [ ] Create a standalone RNS Daemon app for Android
|
||||||
|
- [ ] Network-wide path balancing
|
||||||
- [ ] Add automatic retries to all use cases of the `Request` API
|
- [ ] Add automatic retries to all use cases of the `Request` API
|
||||||
- [ ] Improve performance and efficiency of the `Buffer` and `Channel` API
|
- [ ] Performance and memory optimisations of the Python reference implementation
|
||||||
|
- [ ] Fixing bugs discovered while operating Reticulum systems and applications
|
||||||
|
|
||||||
## Primary Efforts
|
## Primary Efforts
|
||||||
The development path for Reticulum is currently laid out in five distinct areas: *Comprehensibility*, *Universality*, *Functionality*, *Usability & Utility* and *Interfaceability*. Conceptualising the development of Reticulum into these areas serves to advance the implementation and work towards the Foundational Goals & Values of Reticulum.
|
The development path for Reticulum is currently laid out in five distinct areas: *Comprehensibility*, *Universality*, *Functionality*, *Usability & Utility* and *Interfaceability*. Conceptualising the development of Reticulum into these areas serves to advance the implementation and work towards the Foundational Goals & Values of Reticulum.
|
||||||
@@ -38,12 +40,12 @@ These efforts are aimed at improving the ease of which Reticulum is understood,
|
|||||||
- Update NomadNet screenshots
|
- Update NomadNet screenshots
|
||||||
- Update Sideband screenshots
|
- Update Sideband screenshots
|
||||||
- Installation
|
- Installation
|
||||||
- Add a *Reticulum On Raspberry Pi* section
|
- [x] Add a *Reticulum On Raspberry Pi* section
|
||||||
- Update *Reticulum On Android* section if necessary
|
- [x] Update *Reticulum On Android* section if necessary
|
||||||
- Update Android install documentation.
|
- [x] Update Android install documentation.
|
||||||
- Communications hardware section
|
- Communications hardware section
|
||||||
- Add information about RNode external displays.
|
- Add information about RNode external displays.
|
||||||
- Packet radio modems.
|
- [x] Packet radio modems.
|
||||||
- Possibly add other relevant types here as well.
|
- Possibly add other relevant types here as well.
|
||||||
- Setup *Best Practices For...* / *Installation Examples* section.
|
- Setup *Best Practices For...* / *Installation Examples* section.
|
||||||
- Home or office (example)
|
- Home or office (example)
|
||||||
@@ -73,7 +75,6 @@ These efforts aim to expand and improve the core functionality and reliability o
|
|||||||
### Usability & Utility
|
### Usability & Utility
|
||||||
These effors seek to make Reticulum easier to use and operate, and to expand the utility of the stack on deployed systems.
|
These effors seek to make Reticulum easier to use and operate, and to expand the utility of the stack on deployed systems.
|
||||||
|
|
||||||
- Add bluetooth pairing code output to rnodeconf
|
|
||||||
- Easy way to share interface configurations, see [#19](https://github.com/markqvist/Reticulum/discussions/19)
|
- Easy way to share interface configurations, see [#19](https://github.com/markqvist/Reticulum/discussions/19)
|
||||||
- Transit traffic display in rnstatus
|
- Transit traffic display in rnstatus
|
||||||
- rnsconfig utility
|
- rnsconfig utility
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# Sphinx build info version 1
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
config: c5670e7931d15fbef274e2eee17c3561
|
config: 46393e4581f8a313437c181a16088a4d
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ Remote Shell
|
|||||||
|
|
||||||
The `rnsh <https://github.com/acehoss/rnsh>`_ program lets you establish fully interactive
|
The `rnsh <https://github.com/acehoss/rnsh>`_ program lets you establish fully interactive
|
||||||
remote shell sessions over Reticulum. It also allows you to pipe any program to or from a
|
remote shell sessions over Reticulum. It also allows you to pipe any program to or from a
|
||||||
remote system, and is similar to how ``ssh`` works.
|
remote system, and is similar to how ``ssh`` works. The ``rnsh`` is very efficient, and
|
||||||
|
can facilitate fully interactive shell sessions, even over extremely low-bandwidth links.
|
||||||
|
|
||||||
Nomad Network
|
Nomad Network
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
@@ -219,11 +220,11 @@ by adding one of the following interfaces to your ``.reticulum/config`` file:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
# TCP/IP interface to the Dublin hub
|
# TCP/IP interface to the RNS Amsterdam Hub
|
||||||
[[RNS Testnet Dublin]]
|
[[RNS Testnet Amsterdam]]
|
||||||
type = TCPClientInterface
|
type = TCPClientInterface
|
||||||
enabled = yes
|
enabled = yes
|
||||||
target_host = dublin.connect.reticulum.network
|
target_host = amsterdam.connect.reticulum.network
|
||||||
target_port = 4965
|
target_port = 4965
|
||||||
|
|
||||||
# TCP/IP interface to the BetweenTheBorders Hub (community-provided)
|
# TCP/IP interface to the BetweenTheBorders Hub (community-provided)
|
||||||
@@ -233,11 +234,11 @@ by adding one of the following interfaces to your ``.reticulum/config`` file:
|
|||||||
target_host = betweentheborders.com
|
target_host = betweentheborders.com
|
||||||
target_port = 4242
|
target_port = 4242
|
||||||
|
|
||||||
# Interface to I2P hub A
|
# Interface to Testnet I2P Hub
|
||||||
[[RNS Testnet I2P Hub A]]
|
[[RNS Testnet I2P Hub]]
|
||||||
type = I2PInterface
|
type = I2PInterface
|
||||||
enabled = yes
|
enabled = yes
|
||||||
peers = uxg5kubabakh3jtnvsipingbr5574dle7bubvip7llfvwx2tgrua.b32.i2p
|
peers = g3br23bvx3lq5uddcsjii74xgmn6y5q325ovrkq2zw2wbzbqgbuq.b32.i2p
|
||||||
|
|
||||||
Many other Reticulum instances are connecting to this testnet, and you can also join it
|
Many other Reticulum instances are connecting to this testnet, and you can also join it
|
||||||
via other entry points if you know them. There is absolutely no control over the network
|
via other entry points if you know them. There is absolutely no control over the network
|
||||||
@@ -281,7 +282,7 @@ started is to install the latest release of Reticulum via pip:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
pip3 install rns
|
pip install rns
|
||||||
|
|
||||||
The above command will install Reticulum and dependencies, and you will be
|
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
|
ready to import and use RNS in your own programs. The next step will most
|
||||||
@@ -291,7 +292,7 @@ For extended functionality, you can install optional dependencies:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
pip3 install pyserial
|
pip install pyserial
|
||||||
|
|
||||||
|
|
||||||
Further information can be found in the :ref:`API Reference<api-main>`.
|
Further information can be found in the :ref:`API Reference<api-main>`.
|
||||||
@@ -306,7 +307,7 @@ don't use pip, but try this recipe:
|
|||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
pip3 install cryptography pyserial
|
pip install cryptography pyserial
|
||||||
|
|
||||||
# Clone repository
|
# Clone repository
|
||||||
git clone https://github.com/markqvist/Reticulum.git
|
git clone https://github.com/markqvist/Reticulum.git
|
||||||
@@ -316,25 +317,25 @@ don't use pip, but try this recipe:
|
|||||||
ln -s ../RNS ./Examples/
|
ln -s ../RNS ./Examples/
|
||||||
|
|
||||||
# Run an example
|
# Run an example
|
||||||
python3 Examples/Echo.py -s
|
python Examples/Echo.py -s
|
||||||
|
|
||||||
# Unless you've manually created a config file, Reticulum will do so now,
|
# Unless you've manually created a config file, Reticulum will do so now,
|
||||||
# and immediately exit. Make any necessary changes to the file:
|
# and immediately exit. Make any necessary changes to the file:
|
||||||
nano ~/.reticulum/config
|
nano ~/.reticulum/config
|
||||||
|
|
||||||
# ... and launch the example again.
|
# ... and launch the example again.
|
||||||
python3 Examples/Echo.py -s
|
python Examples/Echo.py -s
|
||||||
|
|
||||||
# You can now repeat the process on another computer,
|
# You can now repeat the process on another computer,
|
||||||
# and run the same example with -h to get command line options.
|
# and run the same example with -h to get command line options.
|
||||||
python3 Examples/Echo.py -h
|
python Examples/Echo.py -h
|
||||||
|
|
||||||
# Run the example in client mode to "ping" the server.
|
# Run the example in client mode to "ping" the server.
|
||||||
# Replace the hash below with the actual destination hash of your server.
|
# Replace the hash below with the actual destination hash of your server.
|
||||||
python3 Examples/Echo.py 174a64852a75682259ad8b921b8bf416
|
python Examples/Echo.py 174a64852a75682259ad8b921b8bf416
|
||||||
|
|
||||||
# Have a look at another example
|
# Have a look at another example
|
||||||
python3 Examples/Filetransfer.py -h
|
python Examples/Filetransfer.py -h
|
||||||
|
|
||||||
When you have experimented with the basic examples, it's time to go read the
|
When you have experimented with the basic examples, it's time to go read the
|
||||||
:ref:`Understanding Reticulum<understanding-main>` chapter. Before submitting
|
:ref:`Understanding Reticulum<understanding-main>` chapter. Before submitting
|
||||||
@@ -446,11 +447,11 @@ detailed in this manual.
|
|||||||
|
|
||||||
Debian Bookworm
|
Debian Bookworm
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
On versions of Debian released after April 2023, it is no longer possible
|
On versions of Debian released after April 2023, it is no longer possible by default
|
||||||
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
||||||
use the replacement ``pipx`` command instead, which places installed packages in an
|
use the replacement ``pipx`` command instead, which places installed packages in an
|
||||||
isolated environment. This should not negatively affect Reticulum, but installation
|
isolated environment. This should not negatively affect Reticulum, but will not work
|
||||||
via this method is not fully tested yet.
|
for including and using Reticulum in your own scripts and programs.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
@@ -463,14 +464,28 @@ via this method is not fully tested yet.
|
|||||||
# Install Reticulum
|
# Install Reticulum
|
||||||
pipx install rns
|
pipx install rns
|
||||||
|
|
||||||
|
Alternatively, you can restore normal behaviour to ``pip`` by creating or editing
|
||||||
|
the configuration file located at ``~/.config/pip/pip.conf``, and adding the
|
||||||
|
following section:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
[global]
|
||||||
|
break-system-packages = true
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
Ubuntu Lunar
|
Ubuntu Lunar
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
On versions of Ubuntu released after April 2023, it is no longer possible
|
On versions of Ubuntu released after April 2023, it is no longer possible by default
|
||||||
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
||||||
use the replacement ``pipx`` command instead, which places installed packages in an
|
use the replacement ``pipx`` command instead, which places installed packages in an
|
||||||
isolated environment. This should not negatively affect Reticulum, but installation
|
isolated environment. This should not negatively affect Reticulum, but will not work
|
||||||
via this method is not fully tested yet.
|
for including and using Reticulum in your own scripts and programs.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
@@ -483,14 +498,24 @@ via this method is not fully tested yet.
|
|||||||
# Install Reticulum
|
# Install Reticulum
|
||||||
pipx install rns
|
pipx install rns
|
||||||
|
|
||||||
|
Alternatively, you can restore normal behaviour to ``pip`` by creating or editing
|
||||||
|
the configuration file located at ``~/.config/pip/pip.conf``, and adding the
|
||||||
|
following section:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
[global]
|
||||||
|
break-system-packages = true
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
Pure-Python Reticulum
|
Pure-Python Reticulum
|
||||||
==============================================
|
==============================================
|
||||||
In some rare cases, and on more obscure system types, it is not possible to
|
In some rare cases, and on more obscure system types, it is not possible to
|
||||||
install one or more dependencies
|
install one or more dependencies. In such situations,
|
||||||
|
|
||||||
On more unusual systems, and in some rare cases, it might not be possible to
|
|
||||||
install or even compile one or more of the above modules. In such situations,
|
|
||||||
you can use the ``rnspure`` package instead of the ``rns`` package, or use ``pip``
|
you can use the ``rnspure`` package instead of the ``rns`` package, or use ``pip``
|
||||||
with the ``--no-dependencies`` command-line option. The ``rnspure``
|
with the ``--no-dependencies`` command-line option. The ``rnspure``
|
||||||
package requires no external dependencies for installation. Please note that the
|
package requires no external dependencies for installation. Please note that the
|
||||||
|
|||||||
@@ -24,11 +24,20 @@ starting from scratch.
|
|||||||
This chapter will outline a few different sensible starting paths to get
|
This chapter will outline a few different sensible starting paths to get
|
||||||
real-world functional wireless communications up and running with minimal cost
|
real-world functional wireless communications up and running with minimal cost
|
||||||
and effort. Two fundamental devices categories will be covered, *RNodes* and
|
and effort. Two fundamental devices categories will be covered, *RNodes* and
|
||||||
*WiFi-based radios*.
|
*WiFi-based radios*. Additionally, other common options will be briefly described.
|
||||||
|
|
||||||
|
Knowing how to employ just a few different types of hardware will make it possible
|
||||||
|
to build a wide range of useful networks with little effort.
|
||||||
|
|
||||||
|
Combining Hardware Types
|
||||||
|
========================
|
||||||
|
|
||||||
|
It is useful to combine different link and hardware types when designing and
|
||||||
|
building a network. One useful design pattern is to employ high-capacity point-to-point
|
||||||
|
links based on WiFi or millimeter-wave radios (with high-gain directional antennas)
|
||||||
|
for the network backbone, and using LoRa-based RNodes for covering large areas with
|
||||||
|
connectivity for client devices.
|
||||||
|
|
||||||
While there are many other device categories that are useful in building Reticulum
|
|
||||||
networks, knowing how to employ just these two will make it possible to build
|
|
||||||
a wide range of useful networks with little effort.
|
|
||||||
|
|
||||||
.. _rnode-main:
|
.. _rnode-main:
|
||||||
|
|
||||||
@@ -190,13 +199,6 @@ such as serial port and on-air parameters. For v2.x firmwares, you just need to
|
|||||||
the Connection ID of the RNode, and Reticulum will automatically locate and connect to the
|
the Connection ID of the RNode, and Reticulum will automatically locate and connect to the
|
||||||
RNode, using the parameters stored in the RNode itself.
|
RNode, using the parameters stored in the RNode itself.
|
||||||
|
|
||||||
.. _rnode-suppliers:
|
|
||||||
|
|
||||||
Suppliers
|
|
||||||
^^^^^^^^^
|
|
||||||
Get in touch if you want to have your RNode supplier listed here, or if you want help to
|
|
||||||
get started with producing RNodes.
|
|
||||||
|
|
||||||
|
|
||||||
WiFi-based Hardware
|
WiFi-based Hardware
|
||||||
===================
|
===================
|
||||||
@@ -231,11 +233,31 @@ that is relatively cheap while providing long range and high capacity for Reticu
|
|||||||
networks. As in all other cases, it is also possible for Reticulum to co-exist with IP
|
networks. As in all other cases, it is also possible for Reticulum to co-exist with IP
|
||||||
networks running concurrently on such devices.
|
networks running concurrently on such devices.
|
||||||
|
|
||||||
Combining Hardware Types
|
Ethernet-based Hardware
|
||||||
========================
|
=======================
|
||||||
|
|
||||||
It is useful to combine different link and hardware types when designing and
|
Reticulum can run over any kind of hardware that can provide a switched Ethernet-based
|
||||||
building a network. One useful design pattern is to employ high-capacity point-to-point
|
medium. This means that anything from a plain Ethernet switch, to fiber-optic systems,
|
||||||
links based on WiFi or millimeter-wave radios (with high-gain directional antennas)
|
to data radios with Ethernet interfaces can be used by Reticulum.
|
||||||
for the network backbone, and using LoRa-based RNodes for covering large areas with
|
|
||||||
connectivity for client devices.
|
The Ethernet medium does not need to have any IP infrastructure such as DHCP servers
|
||||||
|
or routing set up, but in case such infrastructure does exist, Reticulum will simply
|
||||||
|
co-exist with.
|
||||||
|
|
||||||
|
To use Reticulum over Ethernet-based mediums, it is generally enough to use the included
|
||||||
|
:ref:`AutoInterface<interfaces-auto>`. This interface also works over any kind of
|
||||||
|
virtual networking adapter, such as ``tun`` and ``tap`` devices in Linux.
|
||||||
|
|
||||||
|
Serial Lines & Devices
|
||||||
|
======================
|
||||||
|
|
||||||
|
Using Reticulum over any kind of raw serial line is also possible with the
|
||||||
|
:ref:`SerialInterface<interfaces-serial>`. This interface type is also useful for
|
||||||
|
using Reticulum over communications hardware that provides a serial port interface.
|
||||||
|
|
||||||
|
Packet Radio Modems
|
||||||
|
===================
|
||||||
|
|
||||||
|
Any packet radio modem that provides a standard KISS interface over USB, serial or TCP
|
||||||
|
can be used with Reticulum. This includes virtual software modems such as
|
||||||
|
`FreeDV TNC <https://github.com/xssfox/freedv-tnc>`_ and `Dire Wolf <https://github.com/wb2osz/direwolf>`_.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
.. _interfaces-main:
|
.. _interfaces-main:
|
||||||
|
|
||||||
********************
|
**********************
|
||||||
Supported Interfaces
|
Configuring Interfaces
|
||||||
********************
|
**********************
|
||||||
|
|
||||||
Reticulum supports using many kinds of devices as networking interfaces, and
|
Reticulum supports using many kinds of devices as networking interfaces, and
|
||||||
allows you to mix and match them in any way you choose. The number of distinct
|
allows you to mix and match them in any way you choose. The number of distinct
|
||||||
@@ -365,6 +365,7 @@ can be used, and offers full control over LoRa parameters.
|
|||||||
# out identification on the channel with
|
# out identification on the channel with
|
||||||
# a set interval by configuring the
|
# a set interval by configuring the
|
||||||
# following two parameters.
|
# following two parameters.
|
||||||
|
|
||||||
# id_callsign = MYCALL-0
|
# id_callsign = MYCALL-0
|
||||||
# id_interval = 600
|
# id_interval = 600
|
||||||
|
|
||||||
@@ -372,7 +373,21 @@ can be used, and offers full control over LoRa parameters.
|
|||||||
# with low amounts of RAM, using packet
|
# with low amounts of RAM, using packet
|
||||||
# flow control can be useful. By default
|
# flow control can be useful. By default
|
||||||
# it is disabled.
|
# it is disabled.
|
||||||
flow_control = False
|
|
||||||
|
# flow_control = False
|
||||||
|
|
||||||
|
# It is possible to limit the airtime
|
||||||
|
# utilisation of an RNode by using the
|
||||||
|
# following two configuration options.
|
||||||
|
# The short-term limit is applied in a
|
||||||
|
# window of approximately 15 seconds,
|
||||||
|
# and the long-term limit is enforced
|
||||||
|
# over a rolling 60 minute window. Both
|
||||||
|
# options are specified in percent.
|
||||||
|
|
||||||
|
# airtime_limit_long = 1.5
|
||||||
|
# airtime_limit_short = 33
|
||||||
|
|
||||||
|
|
||||||
.. _interfaces-serial:
|
.. _interfaces-serial:
|
||||||
|
|
||||||
@@ -746,3 +761,74 @@ conserve bandwidth, while very fast networks can support applications that
|
|||||||
need very frequent announces. Reticulum implements these mechanisms to ensure
|
need very frequent announces. Reticulum implements these mechanisms to ensure
|
||||||
that a large span of network types can seamlessly *co-exist* and interconnect.
|
that a large span of network types can seamlessly *co-exist* and interconnect.
|
||||||
|
|
||||||
|
.. _interfaces-ingress-control:
|
||||||
|
|
||||||
|
New Destination Rate Limiting
|
||||||
|
=============================
|
||||||
|
|
||||||
|
On public interfaces, where anyone may connect and announce new destinations,
|
||||||
|
it can be useful to control the rate at which announces for *new* destinations are
|
||||||
|
processed.
|
||||||
|
|
||||||
|
If a large influx of announces for newly created or previously unknown destinations
|
||||||
|
occur within a short amount of time, Reticulum will place these announces on hold,
|
||||||
|
so that announce traffic for known and previously established destinations can
|
||||||
|
continue to be processed without interruptions.
|
||||||
|
|
||||||
|
After the burst subsides, and an additional waiting period has passed, the held
|
||||||
|
announces will be released at a slow rate, until the hold queue is cleared. This
|
||||||
|
also means, that should a node decide to connect to a public interface, announce
|
||||||
|
a large amount of bogus destinations, and then disconnect, these destination will
|
||||||
|
never make it into path tables and waste network bandwidth on retransmitted
|
||||||
|
announces.
|
||||||
|
|
||||||
|
**It's important to note** that the ingress control works at the level of *individual
|
||||||
|
sub-interfaces*. As an example, this means that one client on a :ref:`TCP Server Interface<interfaces-tcps>`
|
||||||
|
cannot disrupt processing of incoming announces for other connected clients on the same
|
||||||
|
:ref:`TCP Server Interface<interfaces-tcps>`. All other clients on the same interface will still have new announces
|
||||||
|
processed without interruption.
|
||||||
|
|
||||||
|
By default, Reticulum will handle this automatically, and ingress announce
|
||||||
|
control will be enabled on interface where it is sensible to do so. It should
|
||||||
|
generally not be neccessary to modify the ingress control configuration,
|
||||||
|
but all the parameters are exposed for configuration if needed.
|
||||||
|
|
||||||
|
* | The ``ingress_control`` option tells Reticulum whether or not
|
||||||
|
to enable announce ingress control on the interface. Defaults to
|
||||||
|
``True``.
|
||||||
|
|
||||||
|
* | The ``ic_new_time`` option configures how long (in seconds) an
|
||||||
|
interface is considered newly spawned. Defaults to ``2*60*60`` seconds. This
|
||||||
|
option is useful on publicly accessible interfaces that spawn new
|
||||||
|
sub-interfaces when a new client connects.
|
||||||
|
|
||||||
|
* | The ``ic_burst_freq_new`` option sets the maximum announce ingress
|
||||||
|
frequency for newly spawned interfaces. Defaults to ``3.5``
|
||||||
|
announces per second.
|
||||||
|
|
||||||
|
* | The ``ic_burst_freq`` option sets the maximum announce ingress
|
||||||
|
frequency for other interfaces. Defaults to ``12`` announces
|
||||||
|
per second.
|
||||||
|
|
||||||
|
*If an interface exceeds its burst frequency, incoming announces
|
||||||
|
for unknown destinations will be temporarily held in a queue, and
|
||||||
|
not processed until later.*
|
||||||
|
|
||||||
|
* | The ``ic_max_held_announces`` option sets the maximum amount of
|
||||||
|
unique announces that will be held in the queue. Any additional
|
||||||
|
unique announces will be dropped. Defaults to ``256`` announces.
|
||||||
|
|
||||||
|
* | The ``ic_burst_hold`` option sets how much time (in seconds) must
|
||||||
|
pass after the burst frequency drops below its threshold, for the
|
||||||
|
announce burst to be considered cleared. Defaults to ``60``
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
* | The ``ic_burst_penalty`` option sets how much time (in seconds) must
|
||||||
|
pass after the burst is considered cleared, before held announces can
|
||||||
|
start being released from the queue. Defaults to ``5*60``
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
* | The ``ic_held_release_interval`` option sets how much time (in seconds)
|
||||||
|
must pass between releasing each held announce from the queue. Defaults
|
||||||
|
to ``30`` seconds.
|
||||||
|
|
||||||
|
|||||||
@@ -858,7 +858,7 @@ both on general-purpose CPUs and on microcontrollers. The necessary primitives a
|
|||||||
|
|
||||||
* Ed25519 for signatures
|
* Ed25519 for signatures
|
||||||
|
|
||||||
* X22519 for ECDH key exchanges
|
* X25519 for ECDH key exchanges
|
||||||
|
|
||||||
* HKDF for key derivation
|
* HKDF for key derivation
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,17 @@ configuration file is created. The default configuration looks like this:
|
|||||||
instance_control_port = 37429
|
instance_control_port = 37429
|
||||||
|
|
||||||
|
|
||||||
|
# On systems where running instances may not have access
|
||||||
|
# to the same shared Reticulum configuration directory,
|
||||||
|
# it is still possible to allow full interactivity for
|
||||||
|
# running instances, by manually specifying a shared RPC
|
||||||
|
# key. In almost all cases, this option is not needed, but
|
||||||
|
# it can be useful on operating systems such as Android.
|
||||||
|
# The key must be specified as bytes in hexadecimal.
|
||||||
|
|
||||||
|
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
|
||||||
|
|
||||||
|
|
||||||
# You can configure Reticulum to panic and forcibly close
|
# You can configure Reticulum to panic and forcibly close
|
||||||
# if an unrecoverable interface error occurs, such as the
|
# if an unrecoverable interface error occurs, such as the
|
||||||
# hardware device for an interface disappearing. This is
|
# hardware device for an interface disappearing. This is
|
||||||
@@ -108,6 +119,17 @@ configuration file is created. The default configuration looks like this:
|
|||||||
panic_on_interface_error = No
|
panic_on_interface_error = No
|
||||||
|
|
||||||
|
|
||||||
|
# When Transport is enabled, it is possible to allow the
|
||||||
|
# Transport Instance to respond to probe requests from
|
||||||
|
# the rnprobe utility. This can be a useful tool to test
|
||||||
|
# connectivity. When this option is enabled, the probe
|
||||||
|
# destination will be generated from the Identity of the
|
||||||
|
# Transport Instance, and printed to the log at startup.
|
||||||
|
# Optional, and disabled by default.
|
||||||
|
|
||||||
|
respond_to_probes = No
|
||||||
|
|
||||||
|
|
||||||
[logging]
|
[logging]
|
||||||
# Valid log levels are 0 through 7:
|
# Valid log levels are 0 through 7:
|
||||||
# 0: Log only critical information
|
# 0: Log only critical information
|
||||||
@@ -145,10 +167,19 @@ configuration file is created. The default configuration looks like this:
|
|||||||
If Reticulum infrastructure already exists locally, you probably don't need to
|
If Reticulum infrastructure already exists locally, you probably don't need to
|
||||||
change anything, and you may already be connected to a wider network. If not,
|
change anything, and you may already be connected to a wider network. If not,
|
||||||
you will probably need to add relevant *interfaces* to the configuration, in
|
you will probably need to add relevant *interfaces* to the configuration, in
|
||||||
order to communicate with other systems. It is a good idea to read the comments
|
order to communicate with other systems.
|
||||||
and explanations in the above default config. It will teach you the basic
|
|
||||||
concepts you need to understand to configure your network. Once you have done that,
|
You can generate a much more verbose configuration example by running the command:
|
||||||
take a look at the :ref:`Interfaces<interfaces-main>` chapter of this manual.
|
|
||||||
|
``rnsd --exampleconfig``
|
||||||
|
|
||||||
|
The output includes examples for most interface types supported
|
||||||
|
by Reticulum, along with additional options and configuration parameters.
|
||||||
|
|
||||||
|
It is a good idea to read the comments and explanations in the above default config.
|
||||||
|
It will teach you the basic concepts you need to understand to configure your network.
|
||||||
|
Once you have done that, take a look at the :ref:`Interfaces<interfaces-main>` chapter
|
||||||
|
of this manual.
|
||||||
|
|
||||||
Included Utility Programs
|
Included Utility Programs
|
||||||
-------------------------
|
-------------------------
|
||||||
@@ -170,28 +201,47 @@ When ``rnsd`` is running, it will keep all configured interfaces open, handle tr
|
|||||||
it is enabled, and allow any other programs to immediately utilise the
|
it is enabled, and allow any other programs to immediately utilise the
|
||||||
Reticulum network it is configured for.
|
Reticulum network it is configured for.
|
||||||
|
|
||||||
You can even run multiple instances of rnsd with different configurations on
|
You can even run multiple instances of ``rnsd`` with different configurations on
|
||||||
the same system.
|
the same system.
|
||||||
|
|
||||||
.. code:: text
|
**Usage Examples**
|
||||||
|
|
||||||
# Install Reticulum
|
Run ``rnsd``:
|
||||||
pip3 install rns
|
|
||||||
|
|
||||||
# Run rnsd
|
|
||||||
rnsd
|
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnsd [-h] [--config CONFIG] [-v] [-q] [--version]
|
$ rnsd
|
||||||
|
|
||||||
|
[2023-08-18 17:59:56] [Notice] Started rnsd version 0.5.8
|
||||||
|
|
||||||
|
Run ``rnsd`` in service mode, ensuring all logging output is sent directly to file:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnsd -s
|
||||||
|
|
||||||
|
Generate a verbose and detailed configuration example, with explanations of all the
|
||||||
|
various configuration options, and interface configuration examples:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnsd --exampleconfig
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnsd.py [-h] [--config CONFIG] [-v] [-q] [-s] [--exampleconfig] [--version]
|
||||||
|
|
||||||
Reticulum Network Stack Daemon
|
Reticulum Network Stack Daemon
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
-q, --quiet
|
-q, --quiet
|
||||||
|
-s, --service rnsd is running as a service and should log to file
|
||||||
|
--exampleconfig print verbose configuration example to stdout and exit
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
|
|
||||||
You can easily add ``rnsd`` as an always-on service by :ref:`configuring a service<using-systemd>`.
|
You can easily add ``rnsd`` as an always-on service by :ref:`configuring a service<using-systemd>`.
|
||||||
@@ -202,12 +252,14 @@ The rnstatus Utility
|
|||||||
Using the ``rnstatus`` utility, you can view the status of configured Reticulum
|
Using the ``rnstatus`` utility, you can view the status of configured Reticulum
|
||||||
interfaces, similar to the ``ifconfig`` program.
|
interfaces, similar to the ``ifconfig`` program.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Run ``rnstatus``:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnstatus
|
$ rnstatus
|
||||||
rnstatus
|
|
||||||
|
|
||||||
# Example output
|
|
||||||
Shared Instance[37428]
|
Shared Instance[37428]
|
||||||
Status : Up
|
Status : Up
|
||||||
Serving : 1 program
|
Serving : 1 program
|
||||||
@@ -240,44 +292,175 @@ interfaces, similar to the ``ifconfig`` program.
|
|||||||
|
|
||||||
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||||
|
|
||||||
|
Filter output to only show some interfaces:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnstatus [-h] [--config CONFIG] [--version] [-a] [-v]
|
$ rnstatus rnode
|
||||||
|
|
||||||
|
RNodeInterface[RNode UHF]
|
||||||
|
Status : Up
|
||||||
|
Mode : Access Point
|
||||||
|
Rate : 1.30 kbps
|
||||||
|
Access : 64-bit IFAC by <…e702c42ba8>
|
||||||
|
Traffic : 8.49 KB↑
|
||||||
|
9.23 KB↓
|
||||||
|
|
||||||
|
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
|
||||||
|
[-r] [-j] [-v] [filter]
|
||||||
|
|
||||||
Reticulum Network Stack Status
|
Reticulum Network Stack Status
|
||||||
|
|
||||||
optional arguments:
|
positional arguments:
|
||||||
-h, --help show this help message and exit
|
filter only display interfaces with names including filter
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
|
||||||
--version show program's version number and exit
|
options:
|
||||||
-a, --all show all interfaces
|
-h, --help show this help message and exit
|
||||||
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
|
--version show program's version number and exit
|
||||||
|
-a, --all show all interfaces
|
||||||
|
-A, --announce-stats show announce stats
|
||||||
|
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
|
||||||
|
-r, --reverse reverse sorting
|
||||||
|
-j, --json output in JSON format
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
|
|
||||||
|
|
||||||
|
The rnid Utility
|
||||||
|
====================
|
||||||
|
|
||||||
|
With the ``rnid`` utility, you can generate, manage and view Reticulum Identities.
|
||||||
|
The program can also calculate Destination hashes, and perform encryption and
|
||||||
|
decryption of files.
|
||||||
|
|
||||||
|
Using ``rnid``, it is possible to asymmetrically encrypt files and information for
|
||||||
|
any Reticulum destination hash, and also to create and verify cryptographic signatures.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Generate a new Identity:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -g ./new_identity
|
||||||
|
|
||||||
|
Display Identity key information:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -i ./new_identity -p
|
||||||
|
|
||||||
|
Loaded Identity <984b74a3f768bef236af4371e6f248cd> from new_id
|
||||||
|
Public Key : 0f4259fef4521ab75a3409e353fe9073eb10783b4912a6a9937c57bf44a62c1e
|
||||||
|
Private Key : Hidden
|
||||||
|
|
||||||
|
Encrypt a file for an LXMF user:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -i 8dd57a738226809646089335a6b03695 -e my_file.txt
|
||||||
|
|
||||||
|
Recalled Identity <bc7291552be7a58f361522990465165c> for destination <8dd57a738226809646089335a6b03695>
|
||||||
|
Encrypting my_file.txt
|
||||||
|
File my_file.txt encrypted for <bc7291552be7a58f361522990465165c> to my_file.txt.rfe
|
||||||
|
|
||||||
|
If the Identity for the destination is not already known, you can fetch it from the network by using the ``-R`` command-line option:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -R -i 30602def3b3506a28ed33db6f60cc6c9 -e my_file.txt
|
||||||
|
|
||||||
|
Requesting unknown Identity for <30602def3b3506a28ed33db6f60cc6c9>...
|
||||||
|
Received Identity <2b489d06eaf7c543808c76a5332a447d> for destination <30602def3b3506a28ed33db6f60cc6c9> from the network
|
||||||
|
Encrypting my_file.txt
|
||||||
|
File my_file.txt encrypted for <2b489d06eaf7c543808c76a5332a447d> to my_file.txt.rfe
|
||||||
|
|
||||||
|
Decrypt a file using the Reticulum Identity it was encrypted for:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -i ./my_identity -d my_file.txt.rfe
|
||||||
|
|
||||||
|
Loaded Identity <2225fdeecaf6e2db4556c3c2d7637294> from ./my_identity
|
||||||
|
Decrypting ./my_file.txt.rfe...
|
||||||
|
File ./my_file.txt.rfe decrypted with <2225fdeecaf6e2db4556c3c2d7637294> to ./my_file.txt
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnid.py [-h] [--config path] [-i identity] [-g path] [-v] [-q] [-a aspects]
|
||||||
|
[-H aspects] [-e path] [-d path] [-s path] [-V path] [-r path] [-w path]
|
||||||
|
[-f] [-R] [-t seconds] [-p] [-P] [--version]
|
||||||
|
|
||||||
|
Reticulum Identity & Encryption Utility
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--config path path to alternative Reticulum config directory
|
||||||
|
-i identity, --identity identity
|
||||||
|
hexadecimal Reticulum Destination hash or path to Identity file
|
||||||
|
-g path, --generate path
|
||||||
|
generate a new Identity
|
||||||
|
-v, --verbose increase verbosity
|
||||||
|
-q, --quiet decrease verbosity
|
||||||
|
-a aspects, --announce aspects
|
||||||
|
announce a destination based on this Identity
|
||||||
|
-H aspects, --hash aspects
|
||||||
|
show destination hashes for other aspects for this Identity
|
||||||
|
-e path, --encrypt path
|
||||||
|
encrypt file
|
||||||
|
-d path, --decrypt path
|
||||||
|
decrypt file
|
||||||
|
-s path, --sign path sign file
|
||||||
|
-V path, --validate path
|
||||||
|
validate signature
|
||||||
|
-r path, --read path input file path
|
||||||
|
-w path, --write path
|
||||||
|
output file path
|
||||||
|
-f, --force write output even if it overwrites existing files
|
||||||
|
-R, --request request unknown Identities from the network
|
||||||
|
-t seconds identity request timeout before giving up
|
||||||
|
-p, --print-identity print identity info and exit
|
||||||
|
-P, --print-private allow displaying private keys
|
||||||
|
--version show program's version number and exit
|
||||||
|
|
||||||
|
|
||||||
The rnpath Utility
|
The rnpath Utility
|
||||||
====================
|
====================
|
||||||
|
|
||||||
With the ``rnpath`` utility, you can look up and view paths for
|
With the ``rnpath`` utility, you can look up and view paths for
|
||||||
destinations on the Reticulum network.
|
destinations on the Reticulum network.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Resolve path to a destination:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnpath
|
$ rnpath c89b4da064bf66d280f0e4d8abfd9806
|
||||||
rnpath c89b4da064bf66d280f0e4d8abfd9806
|
|
||||||
|
|
||||||
# Example output
|
|
||||||
Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away via <f53a1c4278e0726bb73fcc623d6ce763> on TCPInterface[Testnet/dublin.connect.reticulum.network:4965]
|
Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away via <f53a1c4278e0726bb73fcc623d6ce763> on TCPInterface[Testnet/dublin.connect.reticulum.network:4965]
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D] [-w seconds] [-v] [destination]
|
usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
|
||||||
|
[-x] [-w seconds] [-v] [destination]
|
||||||
|
|
||||||
Reticulum Path Discovery Utility
|
Reticulum Path Discovery Utility
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
destination hexadecimal hash of the destination
|
destination hexadecimal hash of the destination
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
@@ -285,6 +468,7 @@ destinations on the Reticulum network.
|
|||||||
-r, --rates show announce rate info
|
-r, --rates show announce rate info
|
||||||
-d, --drop remove the path to a destination
|
-d, --drop remove the path to a destination
|
||||||
-D, --drop-announces drop all queued announces
|
-D, --drop-announces drop all queued announces
|
||||||
|
-x, --drop-via drop all paths via specified transport instance
|
||||||
-w seconds timeout before giving up
|
-w seconds timeout before giving up
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
|
|
||||||
@@ -295,21 +479,53 @@ The rnprobe Utility
|
|||||||
The ``rnprobe`` utility lets you probe a destination for connectivity, similar
|
The ``rnprobe`` utility lets you probe a destination for connectivity, similar
|
||||||
to the ``ping`` program. Please note that probes will only be answered if the
|
to the ``ping`` program. Please note that probes will only be answered if the
|
||||||
specified destination is configured to send proofs for received packets. Many
|
specified destination is configured to send proofs for received packets. Many
|
||||||
destinations will not have this option enabled, and will not be probable.
|
destinations will not have this option enabled, so most destinations will not
|
||||||
|
be probable.
|
||||||
|
|
||||||
|
You can enable a probe-reply destination on Reticulum Transport Instances by
|
||||||
|
setting the ``respond_to_probes`` configuration directive. Reticulum will then
|
||||||
|
print the probe destination to the log on Transport Instance startup.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Probe a destination:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnprobe
|
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708
|
||||||
rnprobe example_utilities.echo.request 2d03725b327348980d570f739a3a5708
|
|
||||||
|
|
||||||
# Example output
|
|
||||||
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
||||||
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
||||||
Round-trip time is 38.469 milliseconds over 2 hops
|
Round-trip time is 38.469 milliseconds over 2 hops
|
||||||
|
|
||||||
|
Send a larger probe:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708 -s 256
|
||||||
|
|
||||||
|
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
||||||
|
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
||||||
|
Round-trip time is 38.781 milliseconds over 2 hops
|
||||||
|
|
||||||
|
If the interface that receives the probe replies supports reporting radio
|
||||||
|
parameters such as **RSSI** and **SNR**, the ``rnprobe`` utility will print
|
||||||
|
these as part of the result as well.
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnprobe rnstransport.probe e7536ee90bd4a440e130490b87a25124
|
||||||
|
|
||||||
|
Sent 16 byte probe to <e7536ee90bd4a440e130490b87a25124>
|
||||||
|
Valid reply received from <e7536ee90bd4a440e130490b87a25124>
|
||||||
|
Round-trip time is 1.809 seconds over 1 hop [RSSI -73 dBm] [SNR 12.0 dB]
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [-s SIZE]
|
||||||
|
[full_name] [destination_hash]
|
||||||
|
|
||||||
Reticulum Probe Utility
|
Reticulum Probe Utility
|
||||||
|
|
||||||
@@ -320,6 +536,7 @@ destinations will not have this option enabled, and will not be probable.
|
|||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
|
-s SIZE, --size SIZE size of probe packet payload in bytes
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
|
|
||||||
@@ -330,20 +547,39 @@ The rncp Utility
|
|||||||
The ``rncp`` utility is a simple file transfer tool. Using it, you can transfer
|
The ``rncp`` utility is a simple file transfer tool. Using it, you can transfer
|
||||||
files through Reticulum.
|
files through Reticulum.
|
||||||
|
|
||||||
.. code:: text
|
**Usage Examples**
|
||||||
|
|
||||||
# Run rncp on the receiving system, specifying which identities
|
Run rncp on the receiving system, specifying which identities are allowed to send files:
|
||||||
# are allowed to send files
|
|
||||||
rncp --receive -a 1726dbad538775b5bf9b0ea25a4079c8 -a c50cc4e4f7838b6c31f60ab9032cbc62
|
|
||||||
|
|
||||||
# From another system, copy a file to the receiving system
|
|
||||||
rncp ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
|
||||||
|
|
||||||
You can specify as many allowed senders as needed, or complete disable authentication.
|
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rncp [-h] [--config path] [-v] [-q] [-p] [-r] [-b] [-a allowed_hash] [-n] [-w seconds] [--version] [file] [destination]
|
$ rncp --listen -a 1726dbad538775b5bf9b0ea25a4079c8 -a c50cc4e4f7838b6c31f60ab9032cbc62
|
||||||
|
|
||||||
|
You can also specify allowed identity hashes (one per line) in the file ~/.rncp/allowed_identities
|
||||||
|
and simply running the program in listener mode:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rncp --listen
|
||||||
|
|
||||||
|
From another system, copy a file to the receiving system:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rncp ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
||||||
|
|
||||||
|
Or fetch a file from the remote system:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rncp --fetch ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rncp.py [-h] [--config path] [-v] [-q] [-S] [-l] [-f] [-b seconds]
|
||||||
|
[-a allowed_hash] [-n] [-p] [-w seconds] [--version] [file] [destination]
|
||||||
|
|
||||||
Reticulum File Transfer Utility
|
Reticulum File Transfer Utility
|
||||||
|
|
||||||
@@ -351,19 +587,20 @@ You can specify as many allowed senders as needed, or complete disable authentic
|
|||||||
file file to be transferred
|
file file to be transferred
|
||||||
destination hexadecimal hash of the receiver
|
destination hexadecimal hash of the receiver
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config path path to alternative Reticulum config directory
|
--config path path to alternative Reticulum config directory
|
||||||
-v, --verbose increase verbosity
|
-v, --verbose increase verbosity
|
||||||
-q, --quiet decrease verbosity
|
-q, --quiet decrease verbosity
|
||||||
-p, --print-identity print identity and destination info and exit
|
-S, --silent disable transfer progress output
|
||||||
-r, --receive wait for incoming files
|
-l, --listen listen for incoming transfer requests
|
||||||
-b, --no-announce don't announce at program start
|
-f, --fetch fetch file from remote listener instead of sending
|
||||||
|
-b seconds announce interval, 0 to only announce at startup
|
||||||
-a allowed_hash accept from this identity
|
-a allowed_hash accept from this identity
|
||||||
-n, --no-auth accept files from anyone
|
-n, --no-auth accept files and fetches from anyone
|
||||||
|
-p, --print-identity print identity and destination info and exit
|
||||||
-w seconds sender timeout before giving up
|
-w seconds sender timeout before giving up
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-v, --verbose
|
|
||||||
|
|
||||||
|
|
||||||
The rnx Utility
|
The rnx Utility
|
||||||
@@ -371,32 +608,43 @@ The rnx Utility
|
|||||||
|
|
||||||
The ``rnx`` utility is a basic remote command execution program. It allows you to
|
The ``rnx`` utility is a basic remote command execution program. It allows you to
|
||||||
execute commands on remote systems over Reticulum, and to view returned command
|
execute commands on remote systems over Reticulum, and to view returned command
|
||||||
output.
|
output. For a fully interactive remote shell solution, be sure to also take a look
|
||||||
|
at the `rnsh <https://github.com/acehoss/rnsh>`_ program.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Run rnx on the listening system, specifying which identities are allowed to execute commands:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnx on the listening system, specifying which identities
|
$ rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||||
# are allowed to execute commands
|
|
||||||
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
|
||||||
|
|
||||||
# From another system, run a command
|
From another system, run a command on the remote:
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
|
||||||
|
|
||||||
# Or enter the interactive mode pseudo-shell
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 -x
|
|
||||||
|
|
||||||
# The default identity file is stored in
|
|
||||||
# ~/.reticulum/identities/rnx, but you can use
|
|
||||||
# another one, which will be created if it does
|
|
||||||
# not already exist
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 -i /path/to/identity -x
|
|
||||||
|
|
||||||
You can specify as many allowed senders as needed, or completely disable authentication.
|
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-a allowed_hash] [-n] [-N] [-d] [-m] [-w seconds] [-W seconds] [--stdin STDIN] [--stdout STDOUT] [--stderr STDERR] [--version]
|
$ rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
||||||
[destination] [command]
|
|
||||||
|
Or enter the interactive mode pseudo-shell:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnx 7a55144adf826958a9529a3bcf08b149 -x
|
||||||
|
|
||||||
|
The default identity file is stored in ``~/.reticulum/identities/rnx``, but you can use
|
||||||
|
another one, which will be created if it does not already exist
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnx 7a55144adf826958a9529a3bcf08b149 -i /path/to/identity -x
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-n] [-N]
|
||||||
|
[-d] [-m] [-a allowed_hash] [-w seconds] [-W seconds] [--stdin STDIN]
|
||||||
|
[--stdout STDOUT] [--stderr STDERR] [--version] [destination] [command]
|
||||||
|
|
||||||
Reticulum Remote Execution Utility
|
Reticulum Remote Execution Utility
|
||||||
|
|
||||||
@@ -433,11 +681,19 @@ The rnodeconf Utility
|
|||||||
The ``rnodeconf`` utility allows you to inspect and configure existing :ref:`RNodes<rnode-main>`, and
|
The ``rnodeconf`` utility allows you to inspect and configure existing :ref:`RNodes<rnode-main>`, and
|
||||||
to create and provision new :ref:`RNodes<rnode-main>` from any supported hardware devices.
|
to create and provision new :ref:`RNodes<rnode-main>` from any supported hardware devices.
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnodeconf [-h] [-i] [-a] [-u] [-U] [--fw-version version] [--nocheck] [-C] [-N] [-T] [-b] [-B] [-p] [--freq Hz] [--bw Hz] [--txp dBm] [--sf factor] [--cr rate] [--eeprom-backup] [--eeprom-dump] [--eeprom-wipe] [--version] [port]
|
usage: rnodeconf.py [-h] [-i] [-a] [-u] [-U] [--fw-version version] [--nocheck] [-e]
|
||||||
|
[-E] [-C] [--baud-flash baud_flash] [-N] [-T] [-b] [-B] [-p] [-D i]
|
||||||
|
[--freq Hz] [--bw Hz] [--txp dBm] [--sf factor] [--cr rate]
|
||||||
|
[--eeprom-backup] [--eeprom-dump] [--eeprom-wipe] [-P]
|
||||||
|
[--trust-key hexbytes] [--version] [port]
|
||||||
|
|
||||||
RNode Configuration and firmware utility. This program allows you to change various settings and startup modes of RNode. It can also install, flash and update the firmware on supported devices.
|
RNode Configuration and firmware utility. This program allows you to change various
|
||||||
|
settings and startup modes of RNode. It can also install, flash and update the firmware
|
||||||
|
on supported devices.
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
port serial port where RNode is attached
|
port serial port where RNode is attached
|
||||||
@@ -453,11 +709,14 @@ to create and provision new :ref:`RNodes<rnode-main>` from any supported hardwar
|
|||||||
-e, --extract Extract firmware from connected RNode for later use
|
-e, --extract Extract firmware from connected RNode for later use
|
||||||
-E, --use-extracted Use the extracted firmware for autoinstallation or update
|
-E, --use-extracted Use the extracted firmware for autoinstallation or update
|
||||||
-C, --clear-cache Clear locally cached firmware files
|
-C, --clear-cache Clear locally cached firmware files
|
||||||
|
--baud-flash baud_flash
|
||||||
|
Set specific baud rate when flashing device. Default is 921600
|
||||||
-N, --normal Switch device to normal mode
|
-N, --normal Switch device to normal mode
|
||||||
-T, --tnc Switch device to TNC mode
|
-T, --tnc Switch device to TNC mode
|
||||||
-b, --bluetooth-on Turn device bluetooth on
|
-b, --bluetooth-on Turn device bluetooth on
|
||||||
-B, --bluetooth-off Turn device bluetooth off
|
-B, --bluetooth-off Turn device bluetooth off
|
||||||
-p, --bluetooth-pair Put device into bluetooth pairing mode
|
-p, --bluetooth-pair Put device into bluetooth pairing mode
|
||||||
|
-D i, --display i Set display intensity (0-255)
|
||||||
--freq Hz Frequency in Hz for TNC mode
|
--freq Hz Frequency in Hz for TNC mode
|
||||||
--bw Hz Bandwidth in Hz for TNC mode
|
--bw Hz Bandwidth in Hz for TNC mode
|
||||||
--txp dBm TX power in dBm for TNC mode
|
--txp dBm TX power in dBm for TNC mode
|
||||||
@@ -466,6 +725,8 @@ to create and provision new :ref:`RNodes<rnode-main>` from any supported hardwar
|
|||||||
--eeprom-backup Backup EEPROM to file
|
--eeprom-backup Backup EEPROM to file
|
||||||
--eeprom-dump Dump EEPROM to console
|
--eeprom-dump Dump EEPROM to console
|
||||||
--eeprom-wipe Unlock and wipe EEPROM
|
--eeprom-wipe Unlock and wipe EEPROM
|
||||||
|
-P, --public Display public part of signing key
|
||||||
|
--trust-key hexbytes Public key to trust for device verification
|
||||||
--version Print program version and exit
|
--version Print program version and exit
|
||||||
|
|
||||||
For more information on how to create your own RNodes, please read the :ref:`Creating RNodes<rnode-creating>`
|
For more information on how to create your own RNodes, please read the :ref:`Creating RNodes<rnode-creating>`
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ networks, without any need for hierarchical or beaureucratic structures to contr
|
|||||||
or manage them, while ensuring individuals and communities full sovereignty
|
or manage them, while ensuring individuals and communities full sovereignty
|
||||||
over their own network segments.
|
over their own network segments.
|
||||||
|
|
||||||
Reticulum is a complete networking stack, and does not need IP or higher
|
Reticulum is a **complete networking stack**, and does not need IP or higher
|
||||||
layers, although it is easy to utilise IP (with TCP or UDP) as the underlying
|
layers, although it is easy to utilise IP (with TCP or UDP) as the underlying
|
||||||
carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the
|
carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the
|
||||||
Internet or private IP networks. Reticulum is built directly on cryptographic
|
Internet or private IP networks. Reticulum is built directly on cryptographic
|
||||||
principles, allowing resilience and stable functionality in open and trustless
|
principles, allowing resilience and stable functionality in open and trustless
|
||||||
networks.
|
networks.
|
||||||
|
|
||||||
No kernel modules or drivers are required. Reticulum runs completely in
|
No kernel modules or drivers are required. Reticulum can run completely in
|
||||||
userland, and can run on practically any system that runs Python 3. Reticulum
|
userland, and will run on practically any system that runs Python 3. Reticulum
|
||||||
runs well even on small single-board computers like the Pi Zero.
|
runs well even on small single-board computers like the Pi Zero.
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ Current Status
|
|||||||
**Please know!** Reticulum should currently be considered beta software. All core protocol
|
**Please know!** Reticulum should currently be considered beta software. All core protocol
|
||||||
features are implemented and functioning, but additions will probably occur as
|
features are implemented and functioning, but additions will probably occur as
|
||||||
real-world use is explored. *There will be bugs*. The API and wire-format can be
|
real-world use is explored. *There will be bugs*. The API and wire-format can be
|
||||||
considered stable at the moment, but could change if absolutely warranted.
|
considered complete and stable at the moment, but could change if absolutely warranted.
|
||||||
|
|
||||||
|
|
||||||
What does Reticulum Offer?
|
What does Reticulum Offer?
|
||||||
@@ -71,7 +71,7 @@ What does Reticulum Offer?
|
|||||||
|
|
||||||
* Efficient link establishment
|
* Efficient link establishment
|
||||||
|
|
||||||
* Total bandwidth cost of setting up a link is only 3 packets, totalling 297 bytes
|
* Total cost of setting up an encrypted and verified link is only 3 packets, totalling 297 bytes
|
||||||
|
|
||||||
* Low cost of keeping links open at only 0.44 bits per second
|
* Low cost of keeping links open at only 0.44 bits per second
|
||||||
|
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* _sphinx_javascript_frameworks_compat.js
|
|
||||||
* ~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Compatability shim for jQuery and underscores.js.
|
|
||||||
*
|
|
||||||
* WILL BE REMOVED IN Sphinx 6.0
|
|
||||||
* xref RemovedInSphinx60Warning
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* select a different prefix for underscore
|
|
||||||
*/
|
|
||||||
$u = _.noConflict();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* small helper function to urldecode strings
|
|
||||||
*
|
|
||||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
|
|
||||||
*/
|
|
||||||
jQuery.urldecode = function(x) {
|
|
||||||
if (!x) {
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
return decodeURIComponent(x.replace(/\+/g, ' '));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* small helper function to urlencode strings
|
|
||||||
*/
|
|
||||||
jQuery.urlencode = encodeURIComponent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function returns the parsed url parameters of the
|
|
||||||
* current request. Multiple values per key are supported,
|
|
||||||
* it will always return arrays of strings for the value parts.
|
|
||||||
*/
|
|
||||||
jQuery.getQueryParameters = function(s) {
|
|
||||||
if (typeof s === 'undefined')
|
|
||||||
s = document.location.search;
|
|
||||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
|
||||||
var result = {};
|
|
||||||
for (var i = 0; i < parts.length; i++) {
|
|
||||||
var tmp = parts[i].split('=', 2);
|
|
||||||
var key = jQuery.urldecode(tmp[0]);
|
|
||||||
var value = jQuery.urldecode(tmp[1]);
|
|
||||||
if (key in result)
|
|
||||||
result[key].push(value);
|
|
||||||
else
|
|
||||||
result[key] = [value];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* highlight a given string on a jquery object by wrapping it in
|
|
||||||
* span elements with the given class name.
|
|
||||||
*/
|
|
||||||
jQuery.fn.highlightText = function(text, className) {
|
|
||||||
function highlight(node, addItems) {
|
|
||||||
if (node.nodeType === 3) {
|
|
||||||
var val = node.nodeValue;
|
|
||||||
var pos = val.toLowerCase().indexOf(text);
|
|
||||||
if (pos >= 0 &&
|
|
||||||
!jQuery(node.parentNode).hasClass(className) &&
|
|
||||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
|
||||||
var span;
|
|
||||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
|
||||||
if (isInSVG) {
|
|
||||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
|
||||||
} else {
|
|
||||||
span = document.createElement("span");
|
|
||||||
span.className = className;
|
|
||||||
}
|
|
||||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
|
||||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
|
||||||
document.createTextNode(val.substr(pos + text.length)),
|
|
||||||
node.nextSibling));
|
|
||||||
node.nodeValue = val.substr(0, pos);
|
|
||||||
if (isInSVG) {
|
|
||||||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
||||||
var bbox = node.parentElement.getBBox();
|
|
||||||
rect.x.baseVal.value = bbox.x;
|
|
||||||
rect.y.baseVal.value = bbox.y;
|
|
||||||
rect.width.baseVal.value = bbox.width;
|
|
||||||
rect.height.baseVal.value = bbox.height;
|
|
||||||
rect.setAttribute('class', className);
|
|
||||||
addItems.push({
|
|
||||||
"parent": node.parentNode,
|
|
||||||
"target": rect});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!jQuery(node).is("button, select, textarea")) {
|
|
||||||
jQuery.each(node.childNodes, function() {
|
|
||||||
highlight(this, addItems);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var addItems = [];
|
|
||||||
var result = this.each(function() {
|
|
||||||
highlight(this, addItems);
|
|
||||||
});
|
|
||||||
for (var i = 0; i < addItems.length; ++i) {
|
|
||||||
jQuery(addItems[i].parent).before(addItems[i].target);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* backward compatibility for jQuery.browser
|
|
||||||
* This will be supported until firefox bug is fixed.
|
|
||||||
*/
|
|
||||||
if (!jQuery.browser) {
|
|
||||||
jQuery.uaMatch = function(ua) {
|
|
||||||
ua = ua.toLowerCase();
|
|
||||||
|
|
||||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
|
||||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
|
||||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
|
||||||
/(msie) ([\w.]+)/.exec(ua) ||
|
|
||||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
|
||||||
[];
|
|
||||||
|
|
||||||
return {
|
|
||||||
browser: match[ 1 ] || "",
|
|
||||||
version: match[ 2 ] || "0"
|
|
||||||
};
|
|
||||||
};
|
|
||||||
jQuery.browser = {};
|
|
||||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Sphinx stylesheet -- basic theme.
|
* Sphinx stylesheet -- basic theme.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -236,16 +236,6 @@ div.body p, div.body dd, div.body li, div.body blockquote {
|
|||||||
a.headerlink {
|
a.headerlink {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
a.brackets:before,
|
|
||||||
span.brackets > a:before{
|
|
||||||
content: "[";
|
|
||||||
}
|
|
||||||
|
|
||||||
a.brackets:after,
|
|
||||||
span.brackets > a:after {
|
|
||||||
content: "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
h1:hover > a.headerlink,
|
h1:hover > a.headerlink,
|
||||||
h2:hover > a.headerlink,
|
h2:hover > a.headerlink,
|
||||||
@@ -334,11 +324,17 @@ aside.sidebar {
|
|||||||
p.sidebar-title {
|
p.sidebar-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.contents,
|
||||||
|
aside.topic,
|
||||||
div.admonition, div.topic, blockquote {
|
div.admonition, div.topic, blockquote {
|
||||||
clear: left;
|
clear: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- topics ---------------------------------------------------------------- */
|
/* -- topics ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
nav.contents,
|
||||||
|
aside.topic,
|
||||||
div.topic {
|
div.topic {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
@@ -377,6 +373,8 @@ div.body p.centered {
|
|||||||
|
|
||||||
div.sidebar > :last-child,
|
div.sidebar > :last-child,
|
||||||
aside.sidebar > :last-child,
|
aside.sidebar > :last-child,
|
||||||
|
nav.contents > :last-child,
|
||||||
|
aside.topic > :last-child,
|
||||||
div.topic > :last-child,
|
div.topic > :last-child,
|
||||||
div.admonition > :last-child {
|
div.admonition > :last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@@ -384,6 +382,8 @@ div.admonition > :last-child {
|
|||||||
|
|
||||||
div.sidebar::after,
|
div.sidebar::after,
|
||||||
aside.sidebar::after,
|
aside.sidebar::after,
|
||||||
|
nav.contents::after,
|
||||||
|
aside.topic::after,
|
||||||
div.topic::after,
|
div.topic::after,
|
||||||
div.admonition::after,
|
div.admonition::after,
|
||||||
blockquote::after {
|
blockquote::after {
|
||||||
@@ -608,19 +608,27 @@ ol.simple p,
|
|||||||
ul.simple p {
|
ul.simple p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
dl.footnote > dt,
|
|
||||||
dl.citation > dt {
|
|
||||||
float: left;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.footnote > dd,
|
aside.footnote > span,
|
||||||
dl.citation > dd {
|
div.citation > span {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
aside.footnote > span:last-of-type,
|
||||||
|
div.citation > span:last-of-type {
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
aside.footnote > p {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
div.citation > p {
|
||||||
|
margin-left: 4em;
|
||||||
|
}
|
||||||
|
aside.footnote > p:last-of-type,
|
||||||
|
div.citation > p:last-of-type {
|
||||||
margin-bottom: 0em;
|
margin-bottom: 0em;
|
||||||
}
|
}
|
||||||
|
aside.footnote > p:last-of-type:after,
|
||||||
dl.footnote > dd:after,
|
div.citation > p:last-of-type:after {
|
||||||
dl.citation > dd:after {
|
|
||||||
content: "";
|
content: "";
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
@@ -636,10 +644,6 @@ dl.field-list > dt {
|
|||||||
padding-left: 0.5em;
|
padding-left: 0.5em;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
dl.field-list > dt:after {
|
|
||||||
content: ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dl.field-list > dd {
|
dl.field-list > dd {
|
||||||
padding-left: 0.5em;
|
padding-left: 0.5em;
|
||||||
@@ -666,6 +670,16 @@ dd {
|
|||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sig dd {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sig dl {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
dl > dd:last-child,
|
dl > dd:last-child,
|
||||||
dl > dd:last-child > :last-child {
|
dl > dd:last-child > :last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@@ -734,6 +748,14 @@ abbr, acronym {
|
|||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.translated {
|
||||||
|
background-color: rgba(207, 255, 207, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
.untranslated {
|
||||||
|
background-color: rgba(255, 207, 207, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
/* -- code displays --------------------------------------------------------- */
|
/* -- code displays --------------------------------------------------------- */
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ div.highlight {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlight:hover button.copybtn {
|
/* Show the copybutton */
|
||||||
|
.highlight:hover button.copybtn, button.copybtn.success {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const messages = {
|
|||||||
},
|
},
|
||||||
'fr' : {
|
'fr' : {
|
||||||
'copy': 'Copier',
|
'copy': 'Copier',
|
||||||
'copy_to_clipboard': 'Copié dans le presse-papier',
|
'copy_to_clipboard': 'Copier dans le presse-papier',
|
||||||
'copy_success': 'Copié !',
|
'copy_success': 'Copié !',
|
||||||
'copy_failure': 'Échec de la copie',
|
'copy_failure': 'Échec de la copie',
|
||||||
},
|
},
|
||||||
@@ -102,18 +102,25 @@ const clearSelection = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes tooltip text for two seconds, then changes it back
|
// Changes tooltip text for a moment, then changes it back
|
||||||
|
// We want the timeout of our `success` class to be a bit shorter than the
|
||||||
|
// tooltip and icon change, so that we can hide the icon before changing back.
|
||||||
|
var timeoutIcon = 2000;
|
||||||
|
var timeoutSuccessClass = 1500;
|
||||||
|
|
||||||
const temporarilyChangeTooltip = (el, oldText, newText) => {
|
const temporarilyChangeTooltip = (el, oldText, newText) => {
|
||||||
el.setAttribute('data-tooltip', newText)
|
el.setAttribute('data-tooltip', newText)
|
||||||
el.classList.add('success')
|
el.classList.add('success')
|
||||||
setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000)
|
// Remove success a little bit sooner than we change the tooltip
|
||||||
setTimeout(() => el.classList.remove('success'), 2000)
|
// So that we can use CSS to hide the copybutton first
|
||||||
|
setTimeout(() => el.classList.remove('success'), timeoutSuccessClass)
|
||||||
|
setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes the copy button icon for two seconds, then changes it back
|
// Changes the copy button icon for two seconds, then changes it back
|
||||||
const temporarilyChangeIcon = (el) => {
|
const temporarilyChangeIcon = (el) => {
|
||||||
el.innerHTML = iconCheck;
|
el.innerHTML = iconCheck;
|
||||||
setTimeout(() => {el.innerHTML = iconCopy}, 2000)
|
setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addCopyButtonToCodeCells = () => {
|
const addCopyButtonToCodeCells = () => {
|
||||||
@@ -125,7 +132,8 @@ const addCopyButtonToCodeCells = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add copybuttons to all of our code cells
|
// Add copybuttons to all of our code cells
|
||||||
const codeCells = document.querySelectorAll('div.highlight pre')
|
const COPYBUTTON_SELECTOR = 'div.highlight pre';
|
||||||
|
const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR)
|
||||||
codeCells.forEach((codeCell, index) => {
|
codeCells.forEach((codeCell, index) => {
|
||||||
const id = codeCellId(index)
|
const id = codeCellId(index)
|
||||||
codeCell.setAttribute('id', id)
|
codeCell.setAttribute('id', id)
|
||||||
@@ -141,10 +149,25 @@ function escapeRegExp(string) {
|
|||||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes excluded text from a Node.
|
||||||
|
*
|
||||||
|
* @param {Node} target Node to filter.
|
||||||
|
* @param {string} exclude CSS selector of nodes to exclude.
|
||||||
|
* @returns {DOMString} Text from `target` with text removed.
|
||||||
|
*/
|
||||||
|
function filterText(target, exclude) {
|
||||||
|
const clone = target.cloneNode(true); // clone as to not modify the live DOM
|
||||||
|
if (exclude) {
|
||||||
|
// remove excluded nodes
|
||||||
|
clone.querySelectorAll(exclude).forEach(node => node.remove());
|
||||||
|
}
|
||||||
|
return clone.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
// Callback when a copy button is clicked. Will be passed the node that was clicked
|
// Callback when a copy button is clicked. Will be passed the node that was clicked
|
||||||
// should then grab the text and replace pieces of text that shouldn't be used in output
|
// should then grab the text and replace pieces of text that shouldn't be used in output
|
||||||
function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
|
function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
|
||||||
|
|
||||||
var regexp;
|
var regexp;
|
||||||
var match;
|
var match;
|
||||||
|
|
||||||
@@ -199,7 +222,12 @@ function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onl
|
|||||||
|
|
||||||
var copyTargetText = (trigger) => {
|
var copyTargetText = (trigger) => {
|
||||||
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
|
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
|
||||||
return formatCopyText(target.innerText, '', false, true, true, true, '', '')
|
|
||||||
|
// get filtered text
|
||||||
|
let exclude = '.linenos';
|
||||||
|
|
||||||
|
let text = filterText(target, exclude);
|
||||||
|
return formatCopyText(text, '', false, true, true, true, '', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize with a callback so we can modify the text before copy
|
// Initialize with a callback so we can modify the text before copy
|
||||||
|
|||||||
@@ -2,10 +2,25 @@ function escapeRegExp(string) {
|
|||||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes excluded text from a Node.
|
||||||
|
*
|
||||||
|
* @param {Node} target Node to filter.
|
||||||
|
* @param {string} exclude CSS selector of nodes to exclude.
|
||||||
|
* @returns {DOMString} Text from `target` with text removed.
|
||||||
|
*/
|
||||||
|
export function filterText(target, exclude) {
|
||||||
|
const clone = target.cloneNode(true); // clone as to not modify the live DOM
|
||||||
|
if (exclude) {
|
||||||
|
// remove excluded nodes
|
||||||
|
clone.querySelectorAll(exclude).forEach(node => node.remove());
|
||||||
|
}
|
||||||
|
return clone.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
// Callback when a copy button is clicked. Will be passed the node that was clicked
|
// Callback when a copy button is clicked. Will be passed the node that was clicked
|
||||||
// should then grab the text and replace pieces of text that shouldn't be used in output
|
// should then grab the text and replace pieces of text that shouldn't be used in output
|
||||||
export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
|
export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
|
||||||
|
|
||||||
var regexp;
|
var regexp;
|
||||||
var match;
|
var match;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Base JavaScript utilities for all Sphinx HTML documentation.
|
* Base JavaScript utilities for all Sphinx HTML documentation.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||||
VERSION: '0.5.2 beta',
|
VERSION: '0.6.2 beta',
|
||||||
LANGUAGE: 'en',
|
LANGUAGE: 'en',
|
||||||
COLLAPSE_INDEX: false,
|
COLLAPSE_INDEX: false,
|
||||||
BUILDER: 'html',
|
BUILDER: 'html',
|
||||||
|
|||||||
Vendored
-10881
File diff suppressed because it is too large
Load Diff
Vendored
-2
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
|||||||
* This script contains the language-specific data used by searchtools.js,
|
* This script contains the language-specific data used by searchtools.js,
|
||||||
* namely the list of stopwords, stemmer, scorer and splitter.
|
* namely the list of stopwords, stemmer, scorer and splitter.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
|
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
|
||||||
.highlight .gd { color: #a40000 } /* Generic.Deleted */
|
.highlight .gd { color: #a40000 } /* Generic.Deleted */
|
||||||
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
|
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
|
||||||
|
.highlight .ges { color: #000000; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||||
.highlight .gr { color: #ef2929 } /* Generic.Error */
|
.highlight .gr { color: #ef2929 } /* Generic.Error */
|
||||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||||
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
||||||
@@ -101,12 +102,13 @@ body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */
|
|||||||
body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */
|
body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */
|
||||||
body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
|
body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
|
||||||
body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
|
body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
|
||||||
body[data-theme="dark"] .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */
|
body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */
|
||||||
body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
|
body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
|
||||||
body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
|
body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
|
||||||
body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
|
body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
|
||||||
body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */
|
body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */
|
||||||
body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
|
body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
|
||||||
|
body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||||
body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */
|
body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */
|
||||||
body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */
|
body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */
|
||||||
body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */
|
body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */
|
||||||
@@ -186,12 +188,13 @@ body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */
|
|||||||
body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */
|
body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */
|
||||||
body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
|
body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
|
||||||
body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
|
body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
|
||||||
body:not([data-theme="light"]) .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */
|
body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */
|
||||||
body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
|
body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
|
||||||
body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
|
body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
|
||||||
body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
|
body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
|
||||||
body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */
|
body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */
|
||||||
body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
|
body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
|
||||||
|
body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||||
body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */
|
body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */
|
||||||
body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */
|
body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */
|
||||||
body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */
|
body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
/*!
|
|
||||||
* gumshoejs v5.1.2 (patched by @pradyunsg)
|
|
||||||
* A simple, framework-agnostic scrollspy script.
|
|
||||||
* (c) 2019 Chris Ferdinandi
|
|
||||||
* MIT License
|
|
||||||
* http://github.com/cferdinandi/gumshoe
|
|
||||||
*/
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Sphinx JavaScript utilities for the full-text search.
|
* Sphinx JavaScript utilities for the full-text search.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+25
-28
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Code Examples - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Code Examples - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Code Examples</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -258,7 +258,7 @@ program.</p>
|
|||||||
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
|
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
|
||||||
<span class="c1"># and communicated with. Destinations can also announce their</span>
|
<span class="c1"># and communicated with. Destinations can also announce their</span>
|
||||||
<span class="c1"># existence, which will let the network know they are reachable</span>
|
<span class="c1"># existence, which will let the network know they are reachable</span>
|
||||||
<span class="c1"># and autoomatically create paths to them, from anywhere else</span>
|
<span class="c1"># and automatically create paths to them, from anywhere else</span>
|
||||||
<span class="c1"># in the network.</span>
|
<span class="c1"># in the network.</span>
|
||||||
<span class="n">destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
|
<span class="n">destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
|
||||||
<span class="n">identity</span><span class="p">,</span>
|
<span class="n">identity</span><span class="p">,</span>
|
||||||
@@ -269,7 +269,7 @@ program.</p>
|
|||||||
<span class="p">)</span>
|
<span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># We configure the destination to automatically prove all</span>
|
<span class="c1"># We configure the destination to automatically prove all</span>
|
||||||
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
|
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
|
||||||
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
||||||
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
|
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
|
||||||
<span class="c1"># tries to communicate with the destination know whether their</span>
|
<span class="c1"># tries to communicate with the destination know whether their</span>
|
||||||
@@ -375,7 +375,7 @@ notifications about announces from relevant destinations.</p>
|
|||||||
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
|
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
|
||||||
<span class="c1"># and communicated with. Destinations can also announce their</span>
|
<span class="c1"># and communicated with. Destinations can also announce their</span>
|
||||||
<span class="c1"># existence, which will let the network know they are reachable</span>
|
<span class="c1"># existence, which will let the network know they are reachable</span>
|
||||||
<span class="c1"># and autoomatically create paths to them, from anywhere else</span>
|
<span class="c1"># and automatically create paths to them, from anywhere else</span>
|
||||||
<span class="c1"># in the network.</span>
|
<span class="c1"># in the network.</span>
|
||||||
<span class="n">destination_1</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
|
<span class="n">destination_1</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
|
||||||
<span class="n">identity</span><span class="p">,</span>
|
<span class="n">identity</span><span class="p">,</span>
|
||||||
@@ -396,7 +396,7 @@ notifications about announces from relevant destinations.</p>
|
|||||||
<span class="p">)</span>
|
<span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># We configure the destinations to automatically prove all</span>
|
<span class="c1"># We configure the destinations to automatically prove all</span>
|
||||||
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
|
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
|
||||||
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
||||||
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
|
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
|
||||||
<span class="c1"># tries to communicate with the destination know whether their</span>
|
<span class="c1"># tries to communicate with the destination know whether their</span>
|
||||||
@@ -697,7 +697,7 @@ the Packet interface.</p>
|
|||||||
<span class="p">)</span>
|
<span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># We configure the destination to automatically prove all</span>
|
<span class="c1"># We configure the destination to automatically prove all</span>
|
||||||
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
|
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
|
||||||
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
||||||
<span class="c1"># back to the sender of that packet.</span>
|
<span class="c1"># back to the sender of that packet.</span>
|
||||||
<span class="n">echo_destination</span><span class="o">.</span><span class="n">set_proof_strategy</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">PROVE_ALL</span><span class="p">)</span>
|
<span class="n">echo_destination</span><span class="o">.</span><span class="n">set_proof_strategy</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">PROVE_ALL</span><span class="p">)</span>
|
||||||
@@ -2064,7 +2064,7 @@ data between peers of a <code class="docutils literal notranslate"><span class="
|
|||||||
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">"Client disconnected"</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">"Client disconnected"</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="k">def</span> <span class="nf">server_message_received</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
|
<span class="k">def</span> <span class="nf">server_message_received</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
|
||||||
<span class="sd">"""</span>
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
<span class="sd"> A message handler</span>
|
<span class="sd"> A message handler</span>
|
||||||
<span class="sd"> @param message: An instance of a subclass of MessageBase</span>
|
<span class="sd"> @param message: An instance of a subclass of MessageBase</span>
|
||||||
<span class="sd"> @return: True if message was handled</span>
|
<span class="sd"> @return: True if message was handled</span>
|
||||||
@@ -2413,7 +2413,7 @@ binary data between peers of a <code class="docutils literal notranslate"><span
|
|||||||
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">"Client disconnected"</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">"Client disconnected"</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="k">def</span> <span class="nf">server_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>
|
<span class="k">def</span> <span class="nf">server_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>
|
||||||
<span class="sd">"""</span>
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
<span class="sd"> Callback from buffer when buffer has data available</span>
|
<span class="sd"> Callback from buffer when buffer has data available</span>
|
||||||
|
|
||||||
<span class="sd"> :param ready_bytes: The number of bytes ready to read</span>
|
<span class="sd"> :param ready_bytes: The number of bytes ready to read</span>
|
||||||
@@ -3322,14 +3322,11 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+18
-21
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -257,14 +257,11 @@
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+16
-19
@@ -4,12 +4,12 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Index - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -159,16 +159,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -723,14 +723,11 @@
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Getting Started Fast - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Getting Started Fast - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -277,7 +277,8 @@ radio interfaces can then be added later.</p>
|
|||||||
<h3>Remote Shell<a class="headerlink" href="#remote-shell" title="Permalink to this heading">#</a></h3>
|
<h3>Remote Shell<a class="headerlink" href="#remote-shell" title="Permalink to this heading">#</a></h3>
|
||||||
<p>The <a class="reference external" href="https://github.com/acehoss/rnsh">rnsh</a> program lets you establish fully interactive
|
<p>The <a class="reference external" href="https://github.com/acehoss/rnsh">rnsh</a> program lets you establish fully interactive
|
||||||
remote shell sessions over Reticulum. It also allows you to pipe any program to or from a
|
remote shell sessions over Reticulum. It also allows you to pipe any program to or from a
|
||||||
remote system, and is similar to how <code class="docutils literal notranslate"><span class="pre">ssh</span></code> works.</p>
|
remote system, and is similar to how <code class="docutils literal notranslate"><span class="pre">ssh</span></code> works. The <code class="docutils literal notranslate"><span class="pre">rnsh</span></code> is very efficient, and
|
||||||
|
can facilitate fully interactive shell sessions, even over extremely low-bandwidth links.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="nomad-network">
|
<section id="nomad-network">
|
||||||
<h3>Nomad Network<a class="headerlink" href="#nomad-network" title="Permalink to this heading">#</a></h3>
|
<h3>Nomad Network<a class="headerlink" href="#nomad-network" title="Permalink to this heading">#</a></h3>
|
||||||
@@ -387,11 +388,11 @@ easier setup, use TCP.</p>
|
|||||||
<h2>Connect to the Public Testnet<a class="headerlink" href="#connect-to-the-public-testnet" title="Permalink to this heading">#</a></h2>
|
<h2>Connect to the Public Testnet<a class="headerlink" href="#connect-to-the-public-testnet" title="Permalink to this heading">#</a></h2>
|
||||||
<p>An experimental public testnet has been made accessible over both I2P and TCP. You can join it
|
<p>An experimental public testnet has been made accessible over both I2P and TCP. You can join it
|
||||||
by adding one of the following interfaces to your <code class="docutils literal notranslate"><span class="pre">.reticulum/config</span></code> file:</p>
|
by adding one of the following interfaces to your <code class="docutils literal notranslate"><span class="pre">.reticulum/config</span></code> file:</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># TCP/IP interface to the Dublin hub</span>
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># TCP/IP interface to the RNS Amsterdam Hub</span>
|
||||||
<span class="p">[[</span><span class="n">RNS</span> <span class="n">Testnet</span> <span class="n">Dublin</span><span class="p">]]</span>
|
<span class="p">[[</span><span class="n">RNS</span> <span class="n">Testnet</span> <span class="n">Amsterdam</span><span class="p">]]</span>
|
||||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
<span class="nb">type</span> <span class="o">=</span> <span class="n">TCPClientInterface</span>
|
||||||
<span class="n">enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
<span class="n">enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||||
<span class="n">target_host</span> <span class="o">=</span> <span class="n">dublin</span><span class="o">.</span><span class="n">connect</span><span class="o">.</span><span class="n">reticulum</span><span class="o">.</span><span class="n">network</span>
|
<span class="n">target_host</span> <span class="o">=</span> <span class="n">amsterdam</span><span class="o">.</span><span class="n">connect</span><span class="o">.</span><span class="n">reticulum</span><span class="o">.</span><span class="n">network</span>
|
||||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4965</span>
|
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4965</span>
|
||||||
|
|
||||||
<span class="c1"># TCP/IP interface to the BetweenTheBorders Hub (community-provided)</span>
|
<span class="c1"># TCP/IP interface to the BetweenTheBorders Hub (community-provided)</span>
|
||||||
@@ -401,11 +402,11 @@ by adding one of the following interfaces to your <code class="docutils literal
|
|||||||
<span class="n">target_host</span> <span class="o">=</span> <span class="n">betweentheborders</span><span class="o">.</span><span class="n">com</span>
|
<span class="n">target_host</span> <span class="o">=</span> <span class="n">betweentheborders</span><span class="o">.</span><span class="n">com</span>
|
||||||
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
<span class="n">target_port</span> <span class="o">=</span> <span class="mi">4242</span>
|
||||||
|
|
||||||
<span class="c1"># Interface to I2P hub A</span>
|
<span class="c1"># Interface to Testnet I2P Hub</span>
|
||||||
<span class="p">[[</span><span class="n">RNS</span> <span class="n">Testnet</span> <span class="n">I2P</span> <span class="n">Hub</span> <span class="n">A</span><span class="p">]]</span>
|
<span class="p">[[</span><span class="n">RNS</span> <span class="n">Testnet</span> <span class="n">I2P</span> <span class="n">Hub</span><span class="p">]]</span>
|
||||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">I2PInterface</span>
|
<span class="nb">type</span> <span class="o">=</span> <span class="n">I2PInterface</span>
|
||||||
<span class="n">enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
<span class="n">enabled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||||
<span class="n">peers</span> <span class="o">=</span> <span class="n">uxg5kubabakh3jtnvsipingbr5574dle7bubvip7llfvwx2tgrua</span><span class="o">.</span><span class="n">b32</span><span class="o">.</span><span class="n">i2p</span>
|
<span class="n">peers</span> <span class="o">=</span> <span class="n">g3br23bvx3lq5uddcsjii74xgmn6y5q325ovrkq2zw2wbzbqgbuq</span><span class="o">.</span><span class="n">b32</span><span class="o">.</span><span class="n">i2p</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>Many other Reticulum instances are connecting to this testnet, and you can also join it
|
<p>Many other Reticulum instances are connecting to this testnet, and you can also join it
|
||||||
@@ -426,7 +427,7 @@ digital radio transceiver, that integrates easily with Reticulum.</p>
|
|||||||
<p>To build one yourself requires installing a custom firmware on a supported LoRa
|
<p>To build one yourself requires installing a custom firmware on a supported LoRa
|
||||||
development board with an auto-install script. Please see the <a class="reference internal" href="hardware.html#hardware-main"><span class="std std-ref">Communications Hardware</span></a>
|
development board with an auto-install script. Please see the <a class="reference internal" href="hardware.html#hardware-main"><span class="std std-ref">Communications Hardware</span></a>
|
||||||
chapter for a guide. If you prefer purchasing a ready-made unit, you can refer to the
|
chapter for a guide. If you prefer purchasing a ready-made unit, you can refer to the
|
||||||
<a class="reference internal" href="hardware.html#rnode-suppliers"><span class="std std-ref">list of suppliers</span></a>. For more information on RNode, you can also
|
<span class="xref std std-ref">list of suppliers</span>. For more information on RNode, you can also
|
||||||
refer to these additional external resources:</p>
|
refer to these additional external resources:</p>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li><p><a class="reference external" href="https://unsigned.io/how-to-make-your-own-rnodes/">How To Make Your Own RNodes</a></p></li>
|
<li><p><a class="reference external" href="https://unsigned.io/how-to-make-your-own-rnodes/">How To Make Your Own RNodes</a></p></li>
|
||||||
@@ -443,14 +444,14 @@ and propose adding an interface for the hardware.</p>
|
|||||||
<h2>Develop a Program with Reticulum<a class="headerlink" href="#develop-a-program-with-reticulum" title="Permalink to this heading">#</a></h2>
|
<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
|
<p>If you want to develop programs that use Reticulum, the easiest way to get
|
||||||
started is to install the latest release of Reticulum via pip:</p>
|
started is to install the latest release of Reticulum via pip:</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">rns</span>
|
<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>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>The above command will install Reticulum and dependencies, and you will be
|
<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
|
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>
|
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>
|
<p>For extended functionality, you can install optional dependencies:</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">pyserial</span>
|
<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>
|
</pre></div>
|
||||||
</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>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>
|
||||||
@@ -461,7 +462,7 @@ likely be to look at some <a class="reference internal" href="examples.html#exam
|
|||||||
utilities, you’ll want to get the latest source from GitHub. In that case,
|
utilities, you’ll want to get the latest source from GitHub. In that case,
|
||||||
don’t use pip, but try this recipe:</p>
|
don’t use pip, but try this recipe:</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span>
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span>
|
||||||
<span class="n">pip3</span> <span class="n">install</span> <span class="n">cryptography</span> <span class="n">pyserial</span>
|
<span class="n">pip</span> <span class="n">install</span> <span class="n">cryptography</span> <span class="n">pyserial</span>
|
||||||
|
|
||||||
<span class="c1"># Clone repository</span>
|
<span class="c1"># Clone repository</span>
|
||||||
<span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">markqvist</span><span class="o">/</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">git</span>
|
<span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">markqvist</span><span class="o">/</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">git</span>
|
||||||
@@ -471,25 +472,25 @@ don’t use pip, but try this recipe:</p>
|
|||||||
<span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="o">../</span><span class="n">RNS</span> <span class="o">./</span><span class="n">Examples</span><span class="o">/</span>
|
<span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="o">../</span><span class="n">RNS</span> <span class="o">./</span><span class="n">Examples</span><span class="o">/</span>
|
||||||
|
|
||||||
<span class="c1"># Run an example</span>
|
<span class="c1"># Run an example</span>
|
||||||
<span class="n">python3</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">s</span>
|
<span class="n">python</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">s</span>
|
||||||
|
|
||||||
<span class="c1"># Unless you've manually created a config file, Reticulum will do so now,</span>
|
<span class="c1"># Unless you've manually created a config file, Reticulum will do so now,</span>
|
||||||
<span class="c1"># and immediately exit. Make any necessary changes to the file:</span>
|
<span class="c1"># and immediately exit. Make any necessary changes to the file:</span>
|
||||||
<span class="n">nano</span> <span class="o">~/.</span><span class="n">reticulum</span><span class="o">/</span><span class="n">config</span>
|
<span class="n">nano</span> <span class="o">~/.</span><span class="n">reticulum</span><span class="o">/</span><span class="n">config</span>
|
||||||
|
|
||||||
<span class="c1"># ... and launch the example again.</span>
|
<span class="c1"># ... and launch the example again.</span>
|
||||||
<span class="n">python3</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">s</span>
|
<span class="n">python</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">s</span>
|
||||||
|
|
||||||
<span class="c1"># You can now repeat the process on another computer,</span>
|
<span class="c1"># You can now repeat the process on another computer,</span>
|
||||||
<span class="c1"># and run the same example with -h to get command line options.</span>
|
<span class="c1"># and run the same example with -h to get command line options.</span>
|
||||||
<span class="n">python3</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">h</span>
|
<span class="n">python</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">h</span>
|
||||||
|
|
||||||
<span class="c1"># Run the example in client mode to "ping" the server.</span>
|
<span class="c1"># Run the example in client mode to "ping" the server.</span>
|
||||||
<span class="c1"># Replace the hash below with the actual destination hash of your server.</span>
|
<span class="c1"># Replace the hash below with the actual destination hash of your server.</span>
|
||||||
<span class="n">python3</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="mi">174</span><span class="n">a64852a75682259ad8b921b8bf416</span>
|
<span class="n">python</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Echo</span><span class="o">.</span><span class="n">py</span> <span class="mi">174</span><span class="n">a64852a75682259ad8b921b8bf416</span>
|
||||||
|
|
||||||
<span class="c1"># Have a look at another example</span>
|
<span class="c1"># Have a look at another example</span>
|
||||||
<span class="n">python3</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Filetransfer</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">h</span>
|
<span class="n">python</span> <span class="n">Examples</span><span class="o">/</span><span class="n">Filetransfer</span><span class="o">.</span><span class="n">py</span> <span class="o">-</span><span class="n">h</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>When you have experimented with the basic examples, it’s time to go read the
|
<p>When you have experimented with the basic examples, it’s time to go read the
|
||||||
@@ -586,11 +587,11 @@ detailed in this manual.</p>
|
|||||||
</section>
|
</section>
|
||||||
<section id="debian-bookworm">
|
<section id="debian-bookworm">
|
||||||
<h3>Debian Bookworm<a class="headerlink" href="#debian-bookworm" title="Permalink to this heading">#</a></h3>
|
<h3>Debian Bookworm<a class="headerlink" href="#debian-bookworm" title="Permalink to this heading">#</a></h3>
|
||||||
<p>On versions of Debian released after April 2023, it is no longer possible
|
<p>On versions of Debian released after April 2023, it is no longer possible by default
|
||||||
to use <code class="docutils literal notranslate"><span class="pre">pip</span></code> to install packages onto your system. Unfortunately, you will need to
|
to use <code class="docutils literal notranslate"><span class="pre">pip</span></code> to install packages onto your system. Unfortunately, you will need to
|
||||||
use the replacement <code class="docutils literal notranslate"><span class="pre">pipx</span></code> command instead, which places installed packages in an
|
use the replacement <code class="docutils literal notranslate"><span class="pre">pipx</span></code> command instead, which places installed packages in an
|
||||||
isolated environment. This should not negatively affect Reticulum, but installation
|
isolated environment. This should not negatively affect Reticulum, but will not work
|
||||||
via this method is not fully tested yet.</p>
|
for including and using Reticulum in your own scripts and programs.</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install pipx</span>
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install pipx</span>
|
||||||
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">pipx</span>
|
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">pipx</span>
|
||||||
|
|
||||||
@@ -601,14 +602,25 @@ via this method is not fully tested yet.</p>
|
|||||||
<span class="n">pipx</span> <span class="n">install</span> <span class="n">rns</span>
|
<span class="n">pipx</span> <span class="n">install</span> <span class="n">rns</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
|
<p>Alternatively, you can restore normal behaviour to <code class="docutils literal notranslate"><span class="pre">pip</span></code> by creating or editing
|
||||||
|
the configuration file located at <code class="docutils literal notranslate"><span class="pre">~/.config/pip/pip.conf</span></code>, and adding the
|
||||||
|
following section:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[global]
|
||||||
|
break-system-packages = true
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>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 <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>
|
||||||
</section>
|
</section>
|
||||||
<section id="ubuntu-lunar">
|
<section id="ubuntu-lunar">
|
||||||
<h3>Ubuntu Lunar<a class="headerlink" href="#ubuntu-lunar" title="Permalink to this heading">#</a></h3>
|
<h3>Ubuntu Lunar<a class="headerlink" href="#ubuntu-lunar" title="Permalink to this heading">#</a></h3>
|
||||||
<p>On versions of Ubuntu released after April 2023, it is no longer possible
|
<p>On versions of Ubuntu released after April 2023, it is no longer possible by default
|
||||||
to use <code class="docutils literal notranslate"><span class="pre">pip</span></code> to install packages onto your system. Unfortunately, you will need to
|
to use <code class="docutils literal notranslate"><span class="pre">pip</span></code> to install packages onto your system. Unfortunately, you will need to
|
||||||
use the replacement <code class="docutils literal notranslate"><span class="pre">pipx</span></code> command instead, which places installed packages in an
|
use the replacement <code class="docutils literal notranslate"><span class="pre">pipx</span></code> command instead, which places installed packages in an
|
||||||
isolated environment. This should not negatively affect Reticulum, but installation
|
isolated environment. This should not negatively affect Reticulum, but will not work
|
||||||
via this method is not fully tested yet.</p>
|
for including and using Reticulum in your own scripts and programs.</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install pipx</span>
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install pipx</span>
|
||||||
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">pipx</span>
|
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">pipx</span>
|
||||||
|
|
||||||
@@ -619,14 +631,23 @@ via this method is not fully tested yet.</p>
|
|||||||
<span class="n">pipx</span> <span class="n">install</span> <span class="n">rns</span>
|
<span class="n">pipx</span> <span class="n">install</span> <span class="n">rns</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
|
<p>Alternatively, you can restore normal behaviour to <code class="docutils literal notranslate"><span class="pre">pip</span></code> by creating or editing
|
||||||
|
the configuration file located at <code class="docutils literal notranslate"><span class="pre">~/.config/pip/pip.conf</span></code>, and adding the
|
||||||
|
following section:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[global]
|
||||||
|
break-system-packages = true
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>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 <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>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section id="pure-python-reticulum">
|
<section id="pure-python-reticulum">
|
||||||
<h2>Pure-Python Reticulum<a class="headerlink" href="#pure-python-reticulum" title="Permalink to this heading">#</a></h2>
|
<h2>Pure-Python Reticulum<a class="headerlink" href="#pure-python-reticulum" title="Permalink to this heading">#</a></h2>
|
||||||
<p>In some rare cases, and on more obscure system types, it is not possible to
|
<p>In some rare cases, and on more obscure system types, it is not possible to
|
||||||
install one or more dependencies</p>
|
install one or more dependencies. In such situations,
|
||||||
<p>On more unusual systems, and in some rare cases, it might not be possible to
|
|
||||||
install or even compile one or more of the above modules. 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>
|
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>
|
||||||
with the <code class="docutils literal notranslate"><span class="pre">--no-dependencies</span></code> command-line option. The <code class="docutils literal notranslate"><span class="pre">rnspure</span></code>
|
with the <code class="docutils literal notranslate"><span class="pre">--no-dependencies</span></code> command-line option. The <code class="docutils literal notranslate"><span class="pre">rnspure</span></code>
|
||||||
package requires no external dependencies for installation. Please note that the
|
package requires no external dependencies for installation. Please note that the
|
||||||
@@ -737,14 +758,11 @@ section of this manual.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+58
-41
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Supported Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Communications Hardware - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Communications Hardware - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Communications Hardware</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -240,10 +240,17 @@ starting from scratch.</p>
|
|||||||
<p>This chapter will outline a few different sensible starting paths to get
|
<p>This chapter will outline a few different sensible starting paths to get
|
||||||
real-world functional wireless communications up and running with minimal cost
|
real-world functional wireless communications up and running with minimal cost
|
||||||
and effort. Two fundamental devices categories will be covered, <em>RNodes</em> and
|
and effort. Two fundamental devices categories will be covered, <em>RNodes</em> and
|
||||||
<em>WiFi-based radios</em>.</p>
|
<em>WiFi-based radios</em>. Additionally, other common options will be briefly described.</p>
|
||||||
<p>While there are many other device categories that are useful in building Reticulum
|
<p>Knowing how to employ just a few different types of hardware will make it possible
|
||||||
networks, knowing how to employ just these two will make it possible to build
|
to build a wide range of useful networks with little effort.</p>
|
||||||
a wide range of useful networks with little effort.</p>
|
<section id="combining-hardware-types">
|
||||||
|
<h2>Combining Hardware Types<a class="headerlink" href="#combining-hardware-types" title="Permalink to this heading">#</a></h2>
|
||||||
|
<p>It is useful to combine different link and hardware types when designing and
|
||||||
|
building a network. One useful design pattern is to employ high-capacity point-to-point
|
||||||
|
links based on WiFi or millimeter-wave radios (with high-gain directional antennas)
|
||||||
|
for the network backbone, and using LoRa-based RNodes for covering large areas with
|
||||||
|
connectivity for client devices.</p>
|
||||||
|
</section>
|
||||||
<section id="rnode">
|
<section id="rnode">
|
||||||
<span id="rnode-main"></span><h2>RNode<a class="headerlink" href="#rnode" title="Permalink to this heading">#</a></h2>
|
<span id="rnode-main"></span><h2>RNode<a class="headerlink" href="#rnode" title="Permalink to this heading">#</a></h2>
|
||||||
<p>Reliable and general-purpose long-range digital radio transceiver systems are
|
<p>Reliable and general-purpose long-range digital radio transceiver systems are
|
||||||
@@ -373,11 +380,6 @@ such as serial port and on-air parameters. For v2.x firmwares, you just need to
|
|||||||
the Connection ID of the RNode, and Reticulum will automatically locate and connect to the
|
the Connection ID of the RNode, and Reticulum will automatically locate and connect to the
|
||||||
RNode, using the parameters stored in the RNode itself.</p>
|
RNode, using the parameters stored in the RNode itself.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="suppliers">
|
|
||||||
<span id="rnode-suppliers"></span><h3>Suppliers<a class="headerlink" href="#suppliers" title="Permalink to this heading">#</a></h3>
|
|
||||||
<p>Get in touch if you want to have your RNode supplier listed here, or if you want help to
|
|
||||||
get started with producing RNodes.</p>
|
|
||||||
</section>
|
|
||||||
</section>
|
</section>
|
||||||
<section id="wifi-based-hardware">
|
<section id="wifi-based-hardware">
|
||||||
<h2>WiFi-based Hardware<a class="headerlink" href="#wifi-based-hardware" title="Permalink to this heading">#</a></h2>
|
<h2>WiFi-based Hardware<a class="headerlink" href="#wifi-based-hardware" title="Permalink to this heading">#</a></h2>
|
||||||
@@ -403,13 +405,29 @@ that is relatively cheap while providing long range and high capacity for Reticu
|
|||||||
networks. As in all other cases, it is also possible for Reticulum to co-exist with IP
|
networks. As in all other cases, it is also possible for Reticulum to co-exist with IP
|
||||||
networks running concurrently on such devices.</p>
|
networks running concurrently on such devices.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="combining-hardware-types">
|
<section id="ethernet-based-hardware">
|
||||||
<h2>Combining Hardware Types<a class="headerlink" href="#combining-hardware-types" title="Permalink to this heading">#</a></h2>
|
<h2>Ethernet-based Hardware<a class="headerlink" href="#ethernet-based-hardware" title="Permalink to this heading">#</a></h2>
|
||||||
<p>It is useful to combine different link and hardware types when designing and
|
<p>Reticulum can run over any kind of hardware that can provide a switched Ethernet-based
|
||||||
building a network. One useful design pattern is to employ high-capacity point-to-point
|
medium. This means that anything from a plain Ethernet switch, to fiber-optic systems,
|
||||||
links based on WiFi or millimeter-wave radios (with high-gain directional antennas)
|
to data radios with Ethernet interfaces can be used by Reticulum.</p>
|
||||||
for the network backbone, and using LoRa-based RNodes for covering large areas with
|
<p>The Ethernet medium does not need to have any IP infrastructure such as DHCP servers
|
||||||
connectivity for client devices.</p>
|
or routing set up, but in case such infrastructure does exist, Reticulum will simply
|
||||||
|
co-exist with.</p>
|
||||||
|
<p>To use Reticulum over Ethernet-based mediums, it is generally enough to use the included
|
||||||
|
<a class="reference internal" href="interfaces.html#interfaces-auto"><span class="std std-ref">AutoInterface</span></a>. This interface also works over any kind of
|
||||||
|
virtual networking adapter, such as <code class="docutils literal notranslate"><span class="pre">tun</span></code> and <code class="docutils literal notranslate"><span class="pre">tap</span></code> devices in Linux.</p>
|
||||||
|
</section>
|
||||||
|
<section id="serial-lines-devices">
|
||||||
|
<h2>Serial Lines & Devices<a class="headerlink" href="#serial-lines-devices" title="Permalink to this heading">#</a></h2>
|
||||||
|
<p>Using Reticulum over any kind of raw serial line is also possible with the
|
||||||
|
<a class="reference internal" href="interfaces.html#interfaces-serial"><span class="std std-ref">SerialInterface</span></a>. This interface type is also useful for
|
||||||
|
using Reticulum over communications hardware that provides a serial port interface.</p>
|
||||||
|
</section>
|
||||||
|
<section id="packet-radio-modems">
|
||||||
|
<h2>Packet Radio Modems<a class="headerlink" href="#packet-radio-modems" title="Permalink to this heading">#</a></h2>
|
||||||
|
<p>Any packet radio modem that provides a standard KISS interface over USB, serial or TCP
|
||||||
|
can be used with Reticulum. This includes virtual software modems such as
|
||||||
|
<a class="reference external" href="https://github.com/xssfox/freedv-tnc">FreeDV TNC</a> and <a class="reference external" href="https://github.com/wb2osz/direwolf">Dire Wolf</a>.</p>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -423,7 +441,7 @@ connectivity for client devices.</p>
|
|||||||
<div class="context">
|
<div class="context">
|
||||||
<span>Next</span>
|
<span>Next</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">Supported Interfaces</div>
|
<div class="title">Configuring Interfaces</div>
|
||||||
</div>
|
</div>
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
||||||
</a>
|
</a>
|
||||||
@@ -470,6 +488,7 @@ connectivity for client devices.</p>
|
|||||||
<div class="toc-tree">
|
<div class="toc-tree">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="reference internal" href="#">Communications Hardware</a><ul>
|
<li><a class="reference internal" href="#">Communications Hardware</a><ul>
|
||||||
|
<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="#rnode">RNode</a><ul>
|
||||||
<li><a class="reference internal" href="#creating-rnodes">Creating RNodes</a></li>
|
<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">Supported Boards</a><ul>
|
||||||
@@ -483,11 +502,12 @@ connectivity for client devices.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li><a class="reference internal" href="#installation">Installation</a></li>
|
<li><a class="reference internal" href="#installation">Installation</a></li>
|
||||||
<li><a class="reference internal" href="#usage-with-reticulum">Usage with Reticulum</a></li>
|
<li><a class="reference internal" href="#usage-with-reticulum">Usage with Reticulum</a></li>
|
||||||
<li><a class="reference internal" href="#suppliers">Suppliers</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference internal" href="#wifi-based-hardware">WiFi-based Hardware</a></li>
|
<li><a class="reference internal" href="#wifi-based-hardware">WiFi-based Hardware</a></li>
|
||||||
<li><a class="reference internal" href="#combining-hardware-types">Combining Hardware Types</a></li>
|
<li><a class="reference internal" href="#ethernet-based-hardware">Ethernet-based Hardware</a></li>
|
||||||
|
<li><a class="reference internal" href="#serial-lines-devices">Serial Lines & Devices</a></li>
|
||||||
|
<li><a class="reference internal" href="#packet-radio-modems">Packet Radio Modems</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -499,14 +519,11 @@ connectivity for client devices.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+25
-24
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="#"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="#"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="#">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -272,6 +272,7 @@ to participate in the development of Reticulum itself.</p>
|
|||||||
<li class="toctree-l2"><a class="reference internal" href="using.html#included-utility-programs">Included Utility Programs</a><ul>
|
<li class="toctree-l2"><a class="reference internal" href="using.html#included-utility-programs">Included Utility Programs</a><ul>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnsd-utility">The rnsd Utility</a></li>
|
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnsd-utility">The rnsd Utility</a></li>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnstatus-utility">The rnstatus Utility</a></li>
|
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnstatus-utility">The rnstatus Utility</a></li>
|
||||||
|
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnid-utility">The rnid Utility</a></li>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnpath-utility">The rnpath Utility</a></li>
|
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnpath-utility">The rnpath Utility</a></li>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnprobe-utility">The rnprobe Utility</a></li>
|
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnprobe-utility">The rnprobe Utility</a></li>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rncp-utility">The rncp Utility</a></li>
|
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rncp-utility">The rncp Utility</a></li>
|
||||||
@@ -315,19 +316,21 @@ to participate in the development of Reticulum itself.</p>
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a><ul>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a><ul>
|
||||||
|
<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-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#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">Supported Boards</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#installation">Installation</a></li>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="hardware.html#usage-with-reticulum">Usage with Reticulum</a></li>
|
<li class="toctree-l3"><a class="reference internal" href="hardware.html#usage-with-reticulum">Usage with Reticulum</a></li>
|
||||||
<li class="toctree-l3"><a class="reference internal" href="hardware.html#suppliers">Suppliers</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="hardware.html#wifi-based-hardware">WiFi-based Hardware</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="hardware.html#wifi-based-hardware">WiFi-based Hardware</a></li>
|
||||||
<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#ethernet-based-hardware">Ethernet-based Hardware</a></li>
|
||||||
|
<li class="toctree-l2"><a class="reference internal" href="hardware.html#serial-lines-devices">Serial Lines & Devices</a></li>
|
||||||
|
<li class="toctree-l2"><a class="reference internal" href="hardware.html#packet-radio-modems">Packet Radio Modems</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a><ul>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#auto-interface">Auto Interface</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#auto-interface">Auto Interface</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#i2p-interface">I2P Interface</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#i2p-interface">I2P Interface</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#tcp-server-interface">TCP Server Interface</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#tcp-server-interface">TCP Server Interface</a></li>
|
||||||
@@ -341,6 +344,7 @@ to participate in the development of Reticulum itself.</p>
|
|||||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#common-interface-options">Common Interface Options</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#common-interface-options">Common Interface Options</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#interface-modes">Interface Modes</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#interface-modes">Interface Modes</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#announce-rate-control">Announce Rate Control</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#announce-rate-control">Announce Rate Control</a></li>
|
||||||
|
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#new-destination-rate-limiting">New Destination Rate Limiting</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a><ul>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a><ul>
|
||||||
@@ -464,14 +468,11 @@ to participate in the development of Reticulum itself.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+122
-25
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Supported Interfaces - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Configuring Interfaces - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Supported Interfaces</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -221,8 +221,8 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<article role="main">
|
<article role="main">
|
||||||
<section id="supported-interfaces">
|
<section id="configuring-interfaces">
|
||||||
<span id="interfaces-main"></span><h1>Supported Interfaces<a class="headerlink" href="#supported-interfaces" title="Permalink to this heading">#</a></h1>
|
<span id="interfaces-main"></span><h1>Configuring Interfaces<a class="headerlink" href="#configuring-interfaces" title="Permalink to this heading">#</a></h1>
|
||||||
<p>Reticulum supports using many kinds of devices as networking interfaces, and
|
<p>Reticulum supports using many kinds of devices as networking interfaces, and
|
||||||
allows you to mix and match them in any way you choose. The number of distinct
|
allows you to mix and match them in any way you choose. The number of distinct
|
||||||
network topologies you can create with Reticulum is more or less endless, but
|
network topologies you can create with Reticulum is more or less endless, but
|
||||||
@@ -527,6 +527,7 @@ can be used, and offers full control over LoRa parameters.</p>
|
|||||||
<span class="c1"># out identification on the channel with</span>
|
<span class="c1"># out identification on the channel with</span>
|
||||||
<span class="c1"># a set interval by configuring the</span>
|
<span class="c1"># a set interval by configuring the</span>
|
||||||
<span class="c1"># following two parameters.</span>
|
<span class="c1"># following two parameters.</span>
|
||||||
|
|
||||||
<span class="c1"># id_callsign = MYCALL-0</span>
|
<span class="c1"># id_callsign = MYCALL-0</span>
|
||||||
<span class="c1"># id_interval = 600</span>
|
<span class="c1"># id_interval = 600</span>
|
||||||
|
|
||||||
@@ -534,7 +535,20 @@ can be used, and offers full control over LoRa parameters.</p>
|
|||||||
<span class="c1"># with low amounts of RAM, using packet</span>
|
<span class="c1"># with low amounts of RAM, using packet</span>
|
||||||
<span class="c1"># flow control can be useful. By default</span>
|
<span class="c1"># flow control can be useful. By default</span>
|
||||||
<span class="c1"># it is disabled.</span>
|
<span class="c1"># it is disabled.</span>
|
||||||
<span class="n">flow_control</span> <span class="o">=</span> <span class="kc">False</span>
|
|
||||||
|
<span class="c1"># flow_control = False</span>
|
||||||
|
|
||||||
|
<span class="c1"># It is possible to limit the airtime</span>
|
||||||
|
<span class="c1"># utilisation of an RNode by using the</span>
|
||||||
|
<span class="c1"># following two configuration options.</span>
|
||||||
|
<span class="c1"># The short-term limit is applied in a</span>
|
||||||
|
<span class="c1"># window of approximately 15 seconds,</span>
|
||||||
|
<span class="c1"># and the long-term limit is enforced</span>
|
||||||
|
<span class="c1"># over a rolling 60 minute window. Both</span>
|
||||||
|
<span class="c1"># options are specified in percent.</span>
|
||||||
|
|
||||||
|
<span class="c1"># airtime_limit_long = 1.5</span>
|
||||||
|
<span class="c1"># airtime_limit_short = 33</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -923,6 +937,91 @@ conserve bandwidth, while very fast networks can support applications that
|
|||||||
need very frequent announces. Reticulum implements these mechanisms to ensure
|
need very frequent announces. Reticulum implements these mechanisms to ensure
|
||||||
that a large span of network types can seamlessly <em>co-exist</em> and interconnect.</p>
|
that a large span of network types can seamlessly <em>co-exist</em> and interconnect.</p>
|
||||||
</section>
|
</section>
|
||||||
|
<section id="new-destination-rate-limiting">
|
||||||
|
<span id="interfaces-ingress-control"></span><h2>New Destination Rate Limiting<a class="headerlink" href="#new-destination-rate-limiting" title="Permalink to this heading">#</a></h2>
|
||||||
|
<p>On public interfaces, where anyone may connect and announce new destinations,
|
||||||
|
it can be useful to control the rate at which announces for <em>new</em> destinations are
|
||||||
|
processed.</p>
|
||||||
|
<p>If a large influx of announces for newly created or previously unknown destinations
|
||||||
|
occur within a short amount of time, Reticulum will place these announces on hold,
|
||||||
|
so that announce traffic for known and previously established destinations can
|
||||||
|
continue to be processed without interruptions.</p>
|
||||||
|
<p>After the burst subsides, and an additional waiting period has passed, the held
|
||||||
|
announces will be released at a slow rate, until the hold queue is cleared. This
|
||||||
|
also means, that should a node decide to connect to a public interface, announce
|
||||||
|
a large amount of bogus destinations, and then disconnect, these destination will
|
||||||
|
never make it into path tables and waste network bandwidth on retransmitted
|
||||||
|
announces.</p>
|
||||||
|
<p><strong>It’s important to note</strong> that the ingress control works at the level of <em>individual
|
||||||
|
sub-interfaces</em>. As an example, this means that one client on a <a class="reference internal" href="#interfaces-tcps"><span class="std std-ref">TCP Server Interface</span></a>
|
||||||
|
cannot disrupt processing of incoming announces for other connected clients on the same
|
||||||
|
<a class="reference internal" href="#interfaces-tcps"><span class="std std-ref">TCP Server Interface</span></a>. All other clients on the same interface will still have new announces
|
||||||
|
processed without interruption.</p>
|
||||||
|
<p>By default, Reticulum will handle this automatically, and ingress announce
|
||||||
|
control will be enabled on interface where it is sensible to do so. It should
|
||||||
|
generally not be neccessary to modify the ingress control configuration,
|
||||||
|
but all the parameters are exposed for configuration if needed.</p>
|
||||||
|
<blockquote>
|
||||||
|
<div><ul>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ingress_control</span></code> option tells Reticulum whether or not
|
||||||
|
to enable announce ingress control on the interface. Defaults to
|
||||||
|
<code class="docutils literal notranslate"><span class="pre">True</span></code>.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_new_time</span></code> option configures how long (in seconds) an
|
||||||
|
interface is considered newly spawned. Defaults to <code class="docutils literal notranslate"><span class="pre">2*60*60</span></code> seconds. This
|
||||||
|
option is useful on publicly accessible interfaces that spawn new
|
||||||
|
sub-interfaces when a new client connects.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_freq_new</span></code> option sets the maximum announce ingress
|
||||||
|
frequency for newly spawned interfaces. Defaults to <code class="docutils literal notranslate"><span class="pre">3.5</span></code>
|
||||||
|
announces per second.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_freq</span></code> option sets the maximum announce ingress
|
||||||
|
frequency for other interfaces. Defaults to <code class="docutils literal notranslate"><span class="pre">12</span></code> announces
|
||||||
|
per second.</div>
|
||||||
|
</div>
|
||||||
|
<blockquote>
|
||||||
|
<div><p><em>If an interface exceeds its burst frequency, incoming announces
|
||||||
|
for unknown destinations will be temporarily held in a queue, and
|
||||||
|
not processed until later.</em></p>
|
||||||
|
</div></blockquote>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_max_held_announces</span></code> option sets the maximum amount of
|
||||||
|
unique announces that will be held in the queue. Any additional
|
||||||
|
unique announces will be dropped. Defaults to <code class="docutils literal notranslate"><span class="pre">256</span></code> announces.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_hold</span></code> option sets how much time (in seconds) must
|
||||||
|
pass after the burst frequency drops below its threshold, for the
|
||||||
|
announce burst to be considered cleared. Defaults to <code class="docutils literal notranslate"><span class="pre">60</span></code>
|
||||||
|
seconds.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_penalty</span></code> option sets how much time (in seconds) must
|
||||||
|
pass after the burst is considered cleared, before held announces can
|
||||||
|
start being released from the queue. Defaults to <code class="docutils literal notranslate"><span class="pre">5*60</span></code>
|
||||||
|
seconds.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li><div class="line-block">
|
||||||
|
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_held_release_interval</span></code> option sets how much time (in seconds)
|
||||||
|
must pass between releasing each held announce from the queue. Defaults
|
||||||
|
to <code class="docutils literal notranslate"><span class="pre">30</span></code> seconds.</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div></blockquote>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
@@ -981,7 +1080,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
|
|||||||
<div class="toc-tree-container">
|
<div class="toc-tree-container">
|
||||||
<div class="toc-tree">
|
<div class="toc-tree">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="reference internal" href="#">Supported Interfaces</a><ul>
|
<li><a class="reference internal" href="#">Configuring Interfaces</a><ul>
|
||||||
<li><a class="reference internal" href="#auto-interface">Auto Interface</a></li>
|
<li><a class="reference internal" href="#auto-interface">Auto Interface</a></li>
|
||||||
<li><a class="reference internal" href="#i2p-interface">I2P Interface</a></li>
|
<li><a class="reference internal" href="#i2p-interface">I2P Interface</a></li>
|
||||||
<li><a class="reference internal" href="#tcp-server-interface">TCP Server Interface</a></li>
|
<li><a class="reference internal" href="#tcp-server-interface">TCP Server Interface</a></li>
|
||||||
@@ -995,6 +1094,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
|
|||||||
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
|
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
|
||||||
<li><a class="reference internal" href="#interface-modes">Interface Modes</a></li>
|
<li><a class="reference internal" href="#interface-modes">Interface Modes</a></li>
|
||||||
<li><a class="reference internal" href="#announce-rate-control">Announce Rate Control</a></li>
|
<li><a class="reference internal" href="#announce-rate-control">Announce Rate Control</a></li>
|
||||||
|
<li><a class="reference internal" href="#new-destination-rate-limiting">New Destination Rate Limiting</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1006,14 +1106,11 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+20
-23
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Supported Interfaces" href="interfaces.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Building Networks - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Building Networks - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Building Networks</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -413,7 +413,7 @@ connected outliers are now an integral part of the network.</p>
|
|||||||
<span>Previous</span>
|
<span>Previous</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="title">Supported Interfaces</div>
|
<div class="title">Configuring Interfaces</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@@ -467,14 +467,11 @@ connected outliers are now an integral part of the network.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Binary file not shown.
+153
-156
File diff suppressed because it is too large
Load Diff
+15
-18
@@ -4,11 +4,11 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/><title>Search - Reticulum Network Stack 0.5.2 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.6.2 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -158,16 +158,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -262,15 +262,12 @@
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
|
|
||||||
<script src="_static/searchtools.js"></script>
|
<script src="_static/searchtools.js"></script>
|
||||||
<script src="_static/language_data.js"></script>
|
<script src="_static/language_data.js"></script>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+18
-21
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Support Reticulum - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Support Reticulum - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Support Reticulum</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Support Reticulum</a></li>
|
||||||
@@ -330,14 +330,11 @@ report issues, suggest functionality and contribute code to Reticulum.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Understanding Reticulum - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Understanding Reticulum - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Understanding Reticulum</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -1061,7 +1061,7 @@ cryptographic primitives, with widely available implementations that can be used
|
|||||||
both on general-purpose CPUs and on microcontrollers. The necessary primitives are:</p>
|
both on general-purpose CPUs and on microcontrollers. The necessary primitives are:</p>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li><p>Ed25519 for signatures</p></li>
|
<li><p>Ed25519 for signatures</p></li>
|
||||||
<li><p>X22519 for ECDH key exchanges</p></li>
|
<li><p>X25519 for ECDH key exchanges</p></li>
|
||||||
<li><p>HKDF for key derivation</p></li>
|
<li><p>HKDF for key derivation</p></li>
|
||||||
<li><p>Fernet for encrypted tokens</p>
|
<li><p>Fernet for encrypted tokens</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -1196,14 +1196,11 @@ those risks are acceptable to you.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+285
-86
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>Using Reticulum on Your System - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -307,6 +307,17 @@ configuration file is created. The default configuration looks like this:</p>
|
|||||||
<span class="n">instance_control_port</span> <span class="o">=</span> <span class="mi">37429</span>
|
<span class="n">instance_control_port</span> <span class="o">=</span> <span class="mi">37429</span>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="c1"># On systems where running instances may not have access</span>
|
||||||
|
<span class="c1"># to the same shared Reticulum configuration directory,</span>
|
||||||
|
<span class="c1"># it is still possible to allow full interactivity for</span>
|
||||||
|
<span class="c1"># running instances, by manually specifying a shared RPC</span>
|
||||||
|
<span class="c1"># key. In almost all cases, this option is not needed, but</span>
|
||||||
|
<span class="c1"># it can be useful on operating systems such as Android.</span>
|
||||||
|
<span class="c1"># The key must be specified as bytes in hexadecimal.</span>
|
||||||
|
|
||||||
|
<span class="c1"># rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790</span>
|
||||||
|
|
||||||
|
|
||||||
<span class="c1"># You can configure Reticulum to panic and forcibly close</span>
|
<span class="c1"># You can configure Reticulum to panic and forcibly close</span>
|
||||||
<span class="c1"># if an unrecoverable interface error occurs, such as the</span>
|
<span class="c1"># if an unrecoverable interface error occurs, such as the</span>
|
||||||
<span class="c1"># hardware device for an interface disappearing. This is</span>
|
<span class="c1"># hardware device for an interface disappearing. This is</span>
|
||||||
@@ -316,6 +327,17 @@ configuration file is created. The default configuration looks like this:</p>
|
|||||||
<span class="n">panic_on_interface_error</span> <span class="o">=</span> <span class="n">No</span>
|
<span class="n">panic_on_interface_error</span> <span class="o">=</span> <span class="n">No</span>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="c1"># When Transport is enabled, it is possible to allow the</span>
|
||||||
|
<span class="c1"># Transport Instance to respond to probe requests from</span>
|
||||||
|
<span class="c1"># the rnprobe utility. This can be a useful tool to test</span>
|
||||||
|
<span class="c1"># connectivity. When this option is enabled, the probe</span>
|
||||||
|
<span class="c1"># destination will be generated from the Identity of the</span>
|
||||||
|
<span class="c1"># Transport Instance, and printed to the log at startup.</span>
|
||||||
|
<span class="c1"># Optional, and disabled by default.</span>
|
||||||
|
|
||||||
|
<span class="n">respond_to_probes</span> <span class="o">=</span> <span class="n">No</span>
|
||||||
|
|
||||||
|
|
||||||
<span class="p">[</span><span class="n">logging</span><span class="p">]</span>
|
<span class="p">[</span><span class="n">logging</span><span class="p">]</span>
|
||||||
<span class="c1"># Valid log levels are 0 through 7:</span>
|
<span class="c1"># Valid log levels are 0 through 7:</span>
|
||||||
<span class="c1"># 0: Log only critical information</span>
|
<span class="c1"># 0: Log only critical information</span>
|
||||||
@@ -354,10 +376,15 @@ configuration file is created. The default configuration looks like this:</p>
|
|||||||
<p>If Reticulum infrastructure already exists locally, you probably don’t need to
|
<p>If Reticulum infrastructure already exists locally, you probably don’t need to
|
||||||
change anything, and you may already be connected to a wider network. If not,
|
change anything, and you may already be connected to a wider network. If not,
|
||||||
you will probably need to add relevant <em>interfaces</em> to the configuration, in
|
you will probably need to add relevant <em>interfaces</em> to the configuration, in
|
||||||
order to communicate with other systems. It is a good idea to read the comments
|
order to communicate with other systems.</p>
|
||||||
and explanations in the above default config. It will teach you the basic
|
<p>You can generate a much more verbose configuration example by running the command:</p>
|
||||||
concepts you need to understand to configure your network. Once you have done that,
|
<p><code class="docutils literal notranslate"><span class="pre">rnsd</span> <span class="pre">--exampleconfig</span></code></p>
|
||||||
take a look at the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Interfaces</span></a> chapter of this manual.</p>
|
<p>The output includes examples for most interface types supported
|
||||||
|
by Reticulum, along with additional options and configuration parameters.</p>
|
||||||
|
<p>It is a good idea to read the comments and explanations in the above default config.
|
||||||
|
It will teach you the basic concepts you need to understand to configure your network.
|
||||||
|
Once you have done that, take a look at the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Interfaces</span></a> chapter
|
||||||
|
of this manual.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="included-utility-programs">
|
<section id="included-utility-programs">
|
||||||
<h2>Included Utility Programs<a class="headerlink" href="#included-utility-programs" title="Permalink to this heading">#</a></h2>
|
<h2>Included Utility Programs<a class="headerlink" href="#included-utility-programs" title="Permalink to this heading">#</a></h2>
|
||||||
@@ -374,24 +401,36 @@ other programs, applications and services can utilise.</p>
|
|||||||
When <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> is running, it will keep all configured interfaces open, handle transport if
|
When <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> is running, it will keep all configured interfaces open, handle transport if
|
||||||
it is enabled, and allow any other programs to immediately utilise the
|
it is enabled, and allow any other programs to immediately utilise the
|
||||||
Reticulum network it is configured for.</p>
|
Reticulum network it is configured for.</p>
|
||||||
<p>You can even run multiple instances of rnsd with different configurations on
|
<p>You can even run multiple instances of <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> with different configurations on
|
||||||
the same system.</p>
|
the same system.</p>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Install Reticulum
|
<p><strong>Usage Examples</strong></p>
|
||||||
pip3 install rns
|
<p>Run <code class="docutils literal notranslate"><span class="pre">rnsd</span></code>:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnsd
|
||||||
|
|
||||||
# Run rnsd
|
[2023-08-18 17:59:56] [Notice] Started rnsd version 0.5.8
|
||||||
rnsd
|
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnsd [-h] [--config CONFIG] [-v] [-q] [--version]
|
<p>Run <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> in service mode, ensuring all logging output is sent directly to file:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnsd -s
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Generate a verbose and detailed configuration example, with explanations of all the
|
||||||
|
various configuration options, and interface configuration examples:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnsd --exampleconfig
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnsd.py [-h] [--config CONFIG] [-v] [-q] [-s] [--exampleconfig] [--version]
|
||||||
|
|
||||||
Reticulum Network Stack Daemon
|
Reticulum Network Stack Daemon
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
-q, --quiet
|
-q, --quiet
|
||||||
|
-s, --service rnsd is running as a service and should log to file
|
||||||
|
--exampleconfig print verbose configuration example to stdout and exit
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -401,10 +440,10 @@ optional arguments:
|
|||||||
<h3>The rnstatus Utility<a class="headerlink" href="#the-rnstatus-utility" title="Permalink to this heading">#</a></h3>
|
<h3>The rnstatus Utility<a class="headerlink" href="#the-rnstatus-utility" title="Permalink to this heading">#</a></h3>
|
||||||
<p>Using the <code class="docutils literal notranslate"><span class="pre">rnstatus</span></code> utility, you can view the status of configured Reticulum
|
<p>Using the <code class="docutils literal notranslate"><span class="pre">rnstatus</span></code> utility, you can view the status of configured Reticulum
|
||||||
interfaces, similar to the <code class="docutils literal notranslate"><span class="pre">ifconfig</span></code> program.</p>
|
interfaces, similar to the <code class="docutils literal notranslate"><span class="pre">ifconfig</span></code> program.</p>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnstatus
|
<p><strong>Usage Examples</strong></p>
|
||||||
rnstatus
|
<p>Run <code class="docutils literal notranslate"><span class="pre">rnstatus</span></code>:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnstatus
|
||||||
|
|
||||||
# Example output
|
|
||||||
Shared Instance[37428]
|
Shared Instance[37428]
|
||||||
Status : Up
|
Status : Up
|
||||||
Serving : 1 program
|
Serving : 1 program
|
||||||
@@ -438,38 +477,147 @@ RNodeInterface[RNode UHF]
|
|||||||
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus [-h] [--config CONFIG] [--version] [-a] [-v]
|
<p>Filter output to only show some interfaces:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnstatus rnode
|
||||||
|
|
||||||
|
RNodeInterface[RNode UHF]
|
||||||
|
Status : Up
|
||||||
|
Mode : Access Point
|
||||||
|
Rate : 1.30 kbps
|
||||||
|
Access : 64-bit IFAC by <…e702c42ba8>
|
||||||
|
Traffic : 8.49 KB↑
|
||||||
|
9.23 KB↓
|
||||||
|
|
||||||
|
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
|
||||||
|
[-r] [-j] [-v] [filter]
|
||||||
|
|
||||||
Reticulum Network Stack Status
|
Reticulum Network Stack Status
|
||||||
|
|
||||||
optional arguments:
|
positional arguments:
|
||||||
-h, --help show this help message and exit
|
filter only display interfaces with names including filter
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
|
||||||
--version show program's version number and exit
|
options:
|
||||||
-a, --all show all interfaces
|
-h, --help show this help message and exit
|
||||||
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
|
--version show program's version number and exit
|
||||||
|
-a, --all show all interfaces
|
||||||
|
-A, --announce-stats show announce stats
|
||||||
|
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
|
||||||
|
-r, --reverse reverse sorting
|
||||||
|
-j, --json output in JSON format
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<section id="the-rnid-utility">
|
||||||
|
<h3>The rnid Utility<a class="headerlink" href="#the-rnid-utility" title="Permalink to this heading">#</a></h3>
|
||||||
|
<p>With the <code class="docutils literal notranslate"><span class="pre">rnid</span></code> utility, you can generate, manage and view Reticulum Identities.
|
||||||
|
The program can also calculate Destination hashes, and perform encryption and
|
||||||
|
decryption of files.</p>
|
||||||
|
<p>Using <code class="docutils literal notranslate"><span class="pre">rnid</span></code>, it is possible to asymmetrically encrypt files and information for
|
||||||
|
any Reticulum destination hash, and also to create and verify cryptographic signatures.</p>
|
||||||
|
<p><strong>Usage Examples</strong></p>
|
||||||
|
<p>Generate a new Identity:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnid -g ./new_identity
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Display Identity key information:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnid -i ./new_identity -p
|
||||||
|
|
||||||
|
Loaded Identity <984b74a3f768bef236af4371e6f248cd> from new_id
|
||||||
|
Public Key : 0f4259fef4521ab75a3409e353fe9073eb10783b4912a6a9937c57bf44a62c1e
|
||||||
|
Private Key : Hidden
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Encrypt a file for an LXMF user:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnid -i 8dd57a738226809646089335a6b03695 -e my_file.txt
|
||||||
|
|
||||||
|
Recalled Identity <bc7291552be7a58f361522990465165c> for destination <8dd57a738226809646089335a6b03695>
|
||||||
|
Encrypting my_file.txt
|
||||||
|
File my_file.txt encrypted for <bc7291552be7a58f361522990465165c> to my_file.txt.rfe
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>If the Identity for the destination is not already known, you can fetch it from the network by using the <code class="docutils literal notranslate"><span class="pre">-R</span></code> command-line option:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnid -R -i 30602def3b3506a28ed33db6f60cc6c9 -e my_file.txt
|
||||||
|
|
||||||
|
Requesting unknown Identity for <30602def3b3506a28ed33db6f60cc6c9>...
|
||||||
|
Received Identity <2b489d06eaf7c543808c76a5332a447d> for destination <30602def3b3506a28ed33db6f60cc6c9> from the network
|
||||||
|
Encrypting my_file.txt
|
||||||
|
File my_file.txt encrypted for <2b489d06eaf7c543808c76a5332a447d> to my_file.txt.rfe
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Decrypt a file using the Reticulum Identity it was encrypted for:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnid -i ./my_identity -d my_file.txt.rfe
|
||||||
|
|
||||||
|
Loaded Identity <2225fdeecaf6e2db4556c3c2d7637294> from ./my_identity
|
||||||
|
Decrypting ./my_file.txt.rfe...
|
||||||
|
File ./my_file.txt.rfe decrypted with <2225fdeecaf6e2db4556c3c2d7637294> to ./my_file.txt
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnid.py [-h] [--config path] [-i identity] [-g path] [-v] [-q] [-a aspects]
|
||||||
|
[-H aspects] [-e path] [-d path] [-s path] [-V path] [-r path] [-w path]
|
||||||
|
[-f] [-R] [-t seconds] [-p] [-P] [--version]
|
||||||
|
|
||||||
|
Reticulum Identity & Encryption Utility
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--config path path to alternative Reticulum config directory
|
||||||
|
-i identity, --identity identity
|
||||||
|
hexadecimal Reticulum Destination hash or path to Identity file
|
||||||
|
-g path, --generate path
|
||||||
|
generate a new Identity
|
||||||
|
-v, --verbose increase verbosity
|
||||||
|
-q, --quiet decrease verbosity
|
||||||
|
-a aspects, --announce aspects
|
||||||
|
announce a destination based on this Identity
|
||||||
|
-H aspects, --hash aspects
|
||||||
|
show destination hashes for other aspects for this Identity
|
||||||
|
-e path, --encrypt path
|
||||||
|
encrypt file
|
||||||
|
-d path, --decrypt path
|
||||||
|
decrypt file
|
||||||
|
-s path, --sign path sign file
|
||||||
|
-V path, --validate path
|
||||||
|
validate signature
|
||||||
|
-r path, --read path input file path
|
||||||
|
-w path, --write path
|
||||||
|
output file path
|
||||||
|
-f, --force write output even if it overwrites existing files
|
||||||
|
-R, --request request unknown Identities from the network
|
||||||
|
-t seconds identity request timeout before giving up
|
||||||
|
-p, --print-identity print identity info and exit
|
||||||
|
-P, --print-private allow displaying private keys
|
||||||
|
--version show program's version number and exit
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<section id="the-rnpath-utility">
|
<section id="the-rnpath-utility">
|
||||||
<h3>The rnpath Utility<a class="headerlink" href="#the-rnpath-utility" title="Permalink to this heading">#</a></h3>
|
<h3>The rnpath Utility<a class="headerlink" href="#the-rnpath-utility" title="Permalink to this heading">#</a></h3>
|
||||||
<p>With the <code class="docutils literal notranslate"><span class="pre">rnpath</span></code> utility, you can look up and view paths for
|
<p>With the <code class="docutils literal notranslate"><span class="pre">rnpath</span></code> utility, you can look up and view paths for
|
||||||
destinations on the Reticulum network.</p>
|
destinations on the Reticulum network.</p>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnpath
|
<p><strong>Usage Examples</strong></p>
|
||||||
rnpath c89b4da064bf66d280f0e4d8abfd9806
|
<p>Resolve path to a destination:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnpath c89b4da064bf66d280f0e4d8abfd9806
|
||||||
|
|
||||||
# Example output
|
|
||||||
Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away via <f53a1c4278e0726bb73fcc623d6ce763> on TCPInterface[Testnet/dublin.connect.reticulum.network:4965]
|
Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away via <f53a1c4278e0726bb73fcc623d6ce763> on TCPInterface[Testnet/dublin.connect.reticulum.network:4965]
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D] [-w seconds] [-v] [destination]
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
|
||||||
|
[-x] [-w seconds] [-v] [destination]
|
||||||
|
|
||||||
Reticulum Path Discovery Utility
|
Reticulum Path Discovery Utility
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
destination hexadecimal hash of the destination
|
destination hexadecimal hash of the destination
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
@@ -477,6 +625,7 @@ optional arguments:
|
|||||||
-r, --rates show announce rate info
|
-r, --rates show announce rate info
|
||||||
-d, --drop remove the path to a destination
|
-d, --drop remove the path to a destination
|
||||||
-D, --drop-announces drop all queued announces
|
-D, --drop-announces drop all queued announces
|
||||||
|
-x, --drop-via drop all paths via specified transport instance
|
||||||
-w seconds timeout before giving up
|
-w seconds timeout before giving up
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
</pre></div>
|
</pre></div>
|
||||||
@@ -487,17 +636,41 @@ optional arguments:
|
|||||||
<p>The <code class="docutils literal notranslate"><span class="pre">rnprobe</span></code> utility lets you probe a destination for connectivity, similar
|
<p>The <code class="docutils literal notranslate"><span class="pre">rnprobe</span></code> utility lets you probe a destination for connectivity, similar
|
||||||
to the <code class="docutils literal notranslate"><span class="pre">ping</span></code> program. Please note that probes will only be answered if the
|
to the <code class="docutils literal notranslate"><span class="pre">ping</span></code> program. Please note that probes will only be answered if the
|
||||||
specified destination is configured to send proofs for received packets. Many
|
specified destination is configured to send proofs for received packets. Many
|
||||||
destinations will not have this option enabled, and will not be probable.</p>
|
destinations will not have this option enabled, so most destinations will not
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnprobe
|
be probable.</p>
|
||||||
rnprobe example_utilities.echo.request 2d03725b327348980d570f739a3a5708
|
<p>You can enable a probe-reply destination on Reticulum Transport Instances by
|
||||||
|
setting the <code class="docutils literal notranslate"><span class="pre">respond_to_probes</span></code> configuration directive. Reticulum will then
|
||||||
|
print the probe destination to the log on Transport Instance startup.</p>
|
||||||
|
<p><strong>Usage Examples</strong></p>
|
||||||
|
<p>Probe a destination:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708
|
||||||
|
|
||||||
# Example output
|
|
||||||
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
||||||
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
||||||
Round-trip time is 38.469 milliseconds over 2 hops
|
Round-trip time is 38.469 milliseconds over 2 hops
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
<p>Send a larger probe:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708 -s 256
|
||||||
|
|
||||||
|
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
||||||
|
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
||||||
|
Round-trip time is 38.781 milliseconds over 2 hops
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>If the interface that receives the probe replies supports reporting radio
|
||||||
|
parameters such as <strong>RSSI</strong> and <strong>SNR</strong>, the <code class="docutils literal notranslate"><span class="pre">rnprobe</span></code> utility will print
|
||||||
|
these as part of the result as well.</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe rnstransport.probe e7536ee90bd4a440e130490b87a25124
|
||||||
|
|
||||||
|
Sent 16 byte probe to <e7536ee90bd4a440e130490b87a25124>
|
||||||
|
Valid reply received from <e7536ee90bd4a440e130490b87a25124>
|
||||||
|
Round-trip time is 1.809 seconds over 1 hop [RSSI -73 dBm] [SNR 12.0 dB]
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [-s SIZE]
|
||||||
|
[full_name] [destination_hash]
|
||||||
|
|
||||||
Reticulum Probe Utility
|
Reticulum Probe Utility
|
||||||
|
|
||||||
@@ -508,6 +681,7 @@ positional arguments:
|
|||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
|
-s SIZE, --size SIZE size of probe packet payload in bytes
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
</pre></div>
|
</pre></div>
|
||||||
@@ -517,16 +691,27 @@ optional arguments:
|
|||||||
<h3>The rncp Utility<a class="headerlink" href="#the-rncp-utility" title="Permalink to this heading">#</a></h3>
|
<h3>The rncp Utility<a class="headerlink" href="#the-rncp-utility" title="Permalink to this heading">#</a></h3>
|
||||||
<p>The <code class="docutils literal notranslate"><span class="pre">rncp</span></code> utility is a simple file transfer tool. Using it, you can transfer
|
<p>The <code class="docutils literal notranslate"><span class="pre">rncp</span></code> utility is a simple file transfer tool. Using it, you can transfer
|
||||||
files through Reticulum.</p>
|
files through Reticulum.</p>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rncp on the receiving system, specifying which identities
|
<p><strong>Usage Examples</strong></p>
|
||||||
# are allowed to send files
|
<p>Run rncp on the receiving system, specifying which identities are allowed to send files:</p>
|
||||||
rncp --receive -a 1726dbad538775b5bf9b0ea25a4079c8 -a c50cc4e4f7838b6c31f60ab9032cbc62
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rncp --listen -a 1726dbad538775b5bf9b0ea25a4079c8 -a c50cc4e4f7838b6c31f60ab9032cbc62
|
||||||
|
|
||||||
# From another system, copy a file to the receiving system
|
|
||||||
rncp ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>You can specify as many allowed senders as needed, or complete disable authentication.</p>
|
<p>You can also specify allowed identity hashes (one per line) in the file ~/.rncp/allowed_identities
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rncp [-h] [--config path] [-v] [-q] [-p] [-r] [-b] [-a allowed_hash] [-n] [-w seconds] [--version] [file] [destination]
|
and simply running the program in listener mode:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rncp --listen
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>From another system, copy a file to the receiving system:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rncp ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Or fetch a file from the remote system:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rncp --fetch ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rncp.py [-h] [--config path] [-v] [-q] [-S] [-l] [-f] [-b seconds]
|
||||||
|
[-a allowed_hash] [-n] [-p] [-w seconds] [--version] [file] [destination]
|
||||||
|
|
||||||
Reticulum File Transfer Utility
|
Reticulum File Transfer Utility
|
||||||
|
|
||||||
@@ -534,19 +719,20 @@ positional arguments:
|
|||||||
file file to be transferred
|
file file to be transferred
|
||||||
destination hexadecimal hash of the receiver
|
destination hexadecimal hash of the receiver
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config path path to alternative Reticulum config directory
|
--config path path to alternative Reticulum config directory
|
||||||
-v, --verbose increase verbosity
|
-v, --verbose increase verbosity
|
||||||
-q, --quiet decrease verbosity
|
-q, --quiet decrease verbosity
|
||||||
-p, --print-identity print identity and destination info and exit
|
-S, --silent disable transfer progress output
|
||||||
-r, --receive wait for incoming files
|
-l, --listen listen for incoming transfer requests
|
||||||
-b, --no-announce don't announce at program start
|
-f, --fetch fetch file from remote listener instead of sending
|
||||||
|
-b seconds announce interval, 0 to only announce at startup
|
||||||
-a allowed_hash accept from this identity
|
-a allowed_hash accept from this identity
|
||||||
-n, --no-auth accept files from anyone
|
-n, --no-auth accept files and fetches from anyone
|
||||||
|
-p, --print-identity print identity and destination info and exit
|
||||||
-w seconds sender timeout before giving up
|
-w seconds sender timeout before giving up
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-v, --verbose
|
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -554,27 +740,30 @@ optional arguments:
|
|||||||
<h3>The rnx Utility<a class="headerlink" href="#the-rnx-utility" title="Permalink to this heading">#</a></h3>
|
<h3>The rnx Utility<a class="headerlink" href="#the-rnx-utility" title="Permalink to this heading">#</a></h3>
|
||||||
<p>The <code class="docutils literal notranslate"><span class="pre">rnx</span></code> utility is a basic remote command execution program. It allows you to
|
<p>The <code class="docutils literal notranslate"><span class="pre">rnx</span></code> utility is a basic remote command execution program. It allows you to
|
||||||
execute commands on remote systems over Reticulum, and to view returned command
|
execute commands on remote systems over Reticulum, and to view returned command
|
||||||
output.</p>
|
output. For a fully interactive remote shell solution, be sure to also take a look
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnx on the listening system, specifying which identities
|
at the <a class="reference external" href="https://github.com/acehoss/rnsh">rnsh</a> program.</p>
|
||||||
# are allowed to execute commands
|
<p><strong>Usage Examples</strong></p>
|
||||||
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
<p>Run rnx on the listening system, specifying which identities are allowed to execute commands:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||||
# From another system, run a command
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
|
||||||
|
|
||||||
# Or enter the interactive mode pseudo-shell
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 -x
|
|
||||||
|
|
||||||
# The default identity file is stored in
|
|
||||||
# ~/.reticulum/identities/rnx, but you can use
|
|
||||||
# another one, which will be created if it does
|
|
||||||
# not already exist
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 -i /path/to/identity -x
|
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>You can specify as many allowed senders as needed, or completely disable authentication.</p>
|
<p>From another system, run a command on the remote:</p>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-a allowed_hash] [-n] [-N] [-d] [-m] [-w seconds] [-W seconds] [--stdin STDIN] [--stdout STDOUT] [--stderr STDERR] [--version]
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
||||||
[destination] [command]
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Or enter the interactive mode pseudo-shell:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnx 7a55144adf826958a9529a3bcf08b149 -x
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>The default identity file is stored in <code class="docutils literal notranslate"><span class="pre">~/.reticulum/identities/rnx</span></code>, but you can use
|
||||||
|
another one, which will be created if it does not already exist</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnx 7a55144adf826958a9529a3bcf08b149 -i /path/to/identity -x
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-n] [-N]
|
||||||
|
[-d] [-m] [-a allowed_hash] [-w seconds] [-W seconds] [--stdin STDIN]
|
||||||
|
[--stdout STDOUT] [--stderr STDERR] [--version] [destination] [command]
|
||||||
|
|
||||||
Reticulum Remote Execution Utility
|
Reticulum Remote Execution Utility
|
||||||
|
|
||||||
@@ -610,9 +799,16 @@ optional arguments:
|
|||||||
<h3>The rnodeconf Utility<a class="headerlink" href="#the-rnodeconf-utility" title="Permalink to this heading">#</a></h3>
|
<h3>The rnodeconf Utility<a class="headerlink" href="#the-rnodeconf-utility" title="Permalink to this heading">#</a></h3>
|
||||||
<p>The <code class="docutils literal notranslate"><span class="pre">rnodeconf</span></code> utility allows you to inspect and configure existing <a class="reference internal" href="hardware.html#rnode-main"><span class="std std-ref">RNodes</span></a>, and
|
<p>The <code class="docutils literal notranslate"><span class="pre">rnodeconf</span></code> utility allows you to inspect and configure existing <a class="reference internal" href="hardware.html#rnode-main"><span class="std std-ref">RNodes</span></a>, and
|
||||||
to create and provision new <a class="reference internal" href="hardware.html#rnode-main"><span class="std std-ref">RNodes</span></a> from any supported hardware devices.</p>
|
to create and provision new <a class="reference internal" href="hardware.html#rnode-main"><span class="std std-ref">RNodes</span></a> from any supported hardware devices.</p>
|
||||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnodeconf [-h] [-i] [-a] [-u] [-U] [--fw-version version] [--nocheck] [-C] [-N] [-T] [-b] [-B] [-p] [--freq Hz] [--bw Hz] [--txp dBm] [--sf factor] [--cr rate] [--eeprom-backup] [--eeprom-dump] [--eeprom-wipe] [--version] [port]
|
<p><strong>All Command-Line Options</strong></p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnodeconf.py [-h] [-i] [-a] [-u] [-U] [--fw-version version] [--nocheck] [-e]
|
||||||
|
[-E] [-C] [--baud-flash baud_flash] [-N] [-T] [-b] [-B] [-p] [-D i]
|
||||||
|
[--freq Hz] [--bw Hz] [--txp dBm] [--sf factor] [--cr rate]
|
||||||
|
[--eeprom-backup] [--eeprom-dump] [--eeprom-wipe] [-P]
|
||||||
|
[--trust-key hexbytes] [--version] [port]
|
||||||
|
|
||||||
RNode Configuration and firmware utility. This program allows you to change various settings and startup modes of RNode. It can also install, flash and update the firmware on supported devices.
|
RNode Configuration and firmware utility. This program allows you to change various
|
||||||
|
settings and startup modes of RNode. It can also install, flash and update the firmware
|
||||||
|
on supported devices.
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
port serial port where RNode is attached
|
port serial port where RNode is attached
|
||||||
@@ -628,11 +824,14 @@ options:
|
|||||||
-e, --extract Extract firmware from connected RNode for later use
|
-e, --extract Extract firmware from connected RNode for later use
|
||||||
-E, --use-extracted Use the extracted firmware for autoinstallation or update
|
-E, --use-extracted Use the extracted firmware for autoinstallation or update
|
||||||
-C, --clear-cache Clear locally cached firmware files
|
-C, --clear-cache Clear locally cached firmware files
|
||||||
|
--baud-flash baud_flash
|
||||||
|
Set specific baud rate when flashing device. Default is 921600
|
||||||
-N, --normal Switch device to normal mode
|
-N, --normal Switch device to normal mode
|
||||||
-T, --tnc Switch device to TNC mode
|
-T, --tnc Switch device to TNC mode
|
||||||
-b, --bluetooth-on Turn device bluetooth on
|
-b, --bluetooth-on Turn device bluetooth on
|
||||||
-B, --bluetooth-off Turn device bluetooth off
|
-B, --bluetooth-off Turn device bluetooth off
|
||||||
-p, --bluetooth-pair Put device into bluetooth pairing mode
|
-p, --bluetooth-pair Put device into bluetooth pairing mode
|
||||||
|
-D i, --display i Set display intensity (0-255)
|
||||||
--freq Hz Frequency in Hz for TNC mode
|
--freq Hz Frequency in Hz for TNC mode
|
||||||
--bw Hz Bandwidth in Hz for TNC mode
|
--bw Hz Bandwidth in Hz for TNC mode
|
||||||
--txp dBm TX power in dBm for TNC mode
|
--txp dBm TX power in dBm for TNC mode
|
||||||
@@ -641,6 +840,8 @@ options:
|
|||||||
--eeprom-backup Backup EEPROM to file
|
--eeprom-backup Backup EEPROM to file
|
||||||
--eeprom-dump Dump EEPROM to console
|
--eeprom-dump Dump EEPROM to console
|
||||||
--eeprom-wipe Unlock and wipe EEPROM
|
--eeprom-wipe Unlock and wipe EEPROM
|
||||||
|
-P, --public Display public part of signing key
|
||||||
|
--trust-key hexbytes Public key to trust for device verification
|
||||||
--version Print program version and exit
|
--version Print program version and exit
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -789,6 +990,7 @@ WantedBy=multi-user.target
|
|||||||
<li><a class="reference internal" href="#included-utility-programs">Included Utility Programs</a><ul>
|
<li><a class="reference internal" href="#included-utility-programs">Included Utility Programs</a><ul>
|
||||||
<li><a class="reference internal" href="#the-rnsd-utility">The rnsd Utility</a></li>
|
<li><a class="reference internal" href="#the-rnsd-utility">The rnsd Utility</a></li>
|
||||||
<li><a class="reference internal" href="#the-rnstatus-utility">The rnstatus Utility</a></li>
|
<li><a class="reference internal" href="#the-rnstatus-utility">The rnstatus Utility</a></li>
|
||||||
|
<li><a class="reference internal" href="#the-rnid-utility">The rnid Utility</a></li>
|
||||||
<li><a class="reference internal" href="#the-rnpath-utility">The rnpath Utility</a></li>
|
<li><a class="reference internal" href="#the-rnpath-utility">The rnpath Utility</a></li>
|
||||||
<li><a class="reference internal" href="#the-rnprobe-utility">The rnprobe Utility</a></li>
|
<li><a class="reference internal" href="#the-rnprobe-utility">The rnprobe Utility</a></li>
|
||||||
<li><a class="reference internal" href="#the-rncp-utility">The rncp Utility</a></li>
|
<li><a class="reference internal" href="#the-rncp-utility">The rncp Utility</a></li>
|
||||||
@@ -812,14 +1014,11 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+23
-26
@@ -2,16 +2,16 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
<head><meta charset="utf-8"/>
|
<head><meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||||
<title>What is Reticulum? - Reticulum Network Stack 0.5.2 beta documentation</title>
|
<title>What is Reticulum? - Reticulum Network Stack 0.6.2 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,16 +161,16 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
<div class="sidebar-logo-container">
|
<div class="sidebar-logo-container">
|
||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.2 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
<input type="hidden" name="check_keywords" value="yes">
|
||||||
<input type="hidden" name="area" value="default">
|
<input type="hidden" name="area" value="default">
|
||||||
</form>
|
</form>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||||
@@ -238,21 +238,21 @@ outside control, manipulation or censorship.</p>
|
|||||||
networks, without any need for hierarchical or beaureucratic structures to control
|
networks, without any need for hierarchical or beaureucratic structures to control
|
||||||
or manage them, while ensuring individuals and communities full sovereignty
|
or manage them, while ensuring individuals and communities full sovereignty
|
||||||
over their own network segments.</p>
|
over their own network segments.</p>
|
||||||
<p>Reticulum is a complete networking stack, and does not need IP or higher
|
<p>Reticulum is a <strong>complete networking stack</strong>, and does not need IP or higher
|
||||||
layers, although it is easy to utilise IP (with TCP or UDP) as the underlying
|
layers, although it is easy to utilise IP (with TCP or UDP) as the underlying
|
||||||
carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the
|
carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the
|
||||||
Internet or private IP networks. Reticulum is built directly on cryptographic
|
Internet or private IP networks. Reticulum is built directly on cryptographic
|
||||||
principles, allowing resilience and stable functionality in open and trustless
|
principles, allowing resilience and stable functionality in open and trustless
|
||||||
networks.</p>
|
networks.</p>
|
||||||
<p>No kernel modules or drivers are required. Reticulum runs completely in
|
<p>No kernel modules or drivers are required. Reticulum can run completely in
|
||||||
userland, and can run on practically any system that runs Python 3. Reticulum
|
userland, and will run on practically any system that runs Python 3. Reticulum
|
||||||
runs well even on small single-board computers like the Pi Zero.</p>
|
runs well even on small single-board computers like the Pi Zero.</p>
|
||||||
<section id="current-status">
|
<section id="current-status">
|
||||||
<h2>Current Status<a class="headerlink" href="#current-status" title="Permalink to this heading">#</a></h2>
|
<h2>Current Status<a class="headerlink" href="#current-status" title="Permalink to this heading">#</a></h2>
|
||||||
<p><strong>Please know!</strong> Reticulum should currently be considered beta software. All core protocol
|
<p><strong>Please know!</strong> Reticulum should currently be considered beta software. All core protocol
|
||||||
features are implemented and functioning, but additions will probably occur as
|
features are implemented and functioning, but additions will probably occur as
|
||||||
real-world use is explored. <em>There will be bugs</em>. The API and wire-format can be
|
real-world use is explored. <em>There will be bugs</em>. The API and wire-format can be
|
||||||
considered stable at the moment, but could change if absolutely warranted.</p>
|
considered complete and stable at the moment, but could change if absolutely warranted.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="what-does-reticulum-offer">
|
<section id="what-does-reticulum-offer">
|
||||||
<h2>What does Reticulum Offer?<a class="headerlink" href="#what-does-reticulum-offer" title="Permalink to this heading">#</a></h2>
|
<h2>What does Reticulum Offer?<a class="headerlink" href="#what-does-reticulum-offer" title="Permalink to this heading">#</a></h2>
|
||||||
@@ -275,7 +275,7 @@ considered stable at the moment, but could change if absolutely warranted.</p>
|
|||||||
<li><p>An intuitive and developer-friendly API</p></li>
|
<li><p>An intuitive and developer-friendly API</p></li>
|
||||||
<li><p>Efficient link establishment</p>
|
<li><p>Efficient link establishment</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><p>Total bandwidth cost of setting up a link is only 3 packets, totalling 297 bytes</p></li>
|
<li><p>Total cost of setting up an encrypted and verified link is only 3 packets, totalling 297 bytes</p></li>
|
||||||
<li><p>Low cost of keeping links open at only 0.44 bits per second</p></li>
|
<li><p>Low cost of keeping links open at only 0.44 bits per second</p></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -434,14 +434,11 @@ want to help out with this, or can help sponsor an audit, please do get in touch
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
||||||
<script src="_static/jquery.js"></script>
|
<script src="_static/doctools.js?v=888ff710"></script>
|
||||||
<script src="_static/underscore.js"></script>
|
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||||
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||||
<script src="_static/doctools.js"></script>
|
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
<script src="_static/copybutton.js?v=f281be69"></script>
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
<script src="_static/clipboard.min.js"></script>
|
|
||||||
<script src="_static/copybutton.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -70,7 +70,8 @@ Remote Shell
|
|||||||
|
|
||||||
The `rnsh <https://github.com/acehoss/rnsh>`_ program lets you establish fully interactive
|
The `rnsh <https://github.com/acehoss/rnsh>`_ program lets you establish fully interactive
|
||||||
remote shell sessions over Reticulum. It also allows you to pipe any program to or from a
|
remote shell sessions over Reticulum. It also allows you to pipe any program to or from a
|
||||||
remote system, and is similar to how ``ssh`` works.
|
remote system, and is similar to how ``ssh`` works. The ``rnsh`` is very efficient, and
|
||||||
|
can facilitate fully interactive shell sessions, even over extremely low-bandwidth links.
|
||||||
|
|
||||||
Nomad Network
|
Nomad Network
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
@@ -219,11 +220,11 @@ by adding one of the following interfaces to your ``.reticulum/config`` file:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
# TCP/IP interface to the Dublin hub
|
# TCP/IP interface to the RNS Amsterdam Hub
|
||||||
[[RNS Testnet Dublin]]
|
[[RNS Testnet Amsterdam]]
|
||||||
type = TCPClientInterface
|
type = TCPClientInterface
|
||||||
enabled = yes
|
enabled = yes
|
||||||
target_host = dublin.connect.reticulum.network
|
target_host = amsterdam.connect.reticulum.network
|
||||||
target_port = 4965
|
target_port = 4965
|
||||||
|
|
||||||
# TCP/IP interface to the BetweenTheBorders Hub (community-provided)
|
# TCP/IP interface to the BetweenTheBorders Hub (community-provided)
|
||||||
@@ -233,11 +234,11 @@ by adding one of the following interfaces to your ``.reticulum/config`` file:
|
|||||||
target_host = betweentheborders.com
|
target_host = betweentheborders.com
|
||||||
target_port = 4242
|
target_port = 4242
|
||||||
|
|
||||||
# Interface to I2P hub A
|
# Interface to Testnet I2P Hub
|
||||||
[[RNS Testnet I2P Hub A]]
|
[[RNS Testnet I2P Hub]]
|
||||||
type = I2PInterface
|
type = I2PInterface
|
||||||
enabled = yes
|
enabled = yes
|
||||||
peers = uxg5kubabakh3jtnvsipingbr5574dle7bubvip7llfvwx2tgrua.b32.i2p
|
peers = g3br23bvx3lq5uddcsjii74xgmn6y5q325ovrkq2zw2wbzbqgbuq.b32.i2p
|
||||||
|
|
||||||
Many other Reticulum instances are connecting to this testnet, and you can also join it
|
Many other Reticulum instances are connecting to this testnet, and you can also join it
|
||||||
via other entry points if you know them. There is absolutely no control over the network
|
via other entry points if you know them. There is absolutely no control over the network
|
||||||
@@ -281,7 +282,7 @@ started is to install the latest release of Reticulum via pip:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
pip3 install rns
|
pip install rns
|
||||||
|
|
||||||
The above command will install Reticulum and dependencies, and you will be
|
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
|
ready to import and use RNS in your own programs. The next step will most
|
||||||
@@ -291,7 +292,7 @@ For extended functionality, you can install optional dependencies:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
pip3 install pyserial
|
pip install pyserial
|
||||||
|
|
||||||
|
|
||||||
Further information can be found in the :ref:`API Reference<api-main>`.
|
Further information can be found in the :ref:`API Reference<api-main>`.
|
||||||
@@ -306,7 +307,7 @@ don't use pip, but try this recipe:
|
|||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
pip3 install cryptography pyserial
|
pip install cryptography pyserial
|
||||||
|
|
||||||
# Clone repository
|
# Clone repository
|
||||||
git clone https://github.com/markqvist/Reticulum.git
|
git clone https://github.com/markqvist/Reticulum.git
|
||||||
@@ -316,25 +317,25 @@ don't use pip, but try this recipe:
|
|||||||
ln -s ../RNS ./Examples/
|
ln -s ../RNS ./Examples/
|
||||||
|
|
||||||
# Run an example
|
# Run an example
|
||||||
python3 Examples/Echo.py -s
|
python Examples/Echo.py -s
|
||||||
|
|
||||||
# Unless you've manually created a config file, Reticulum will do so now,
|
# Unless you've manually created a config file, Reticulum will do so now,
|
||||||
# and immediately exit. Make any necessary changes to the file:
|
# and immediately exit. Make any necessary changes to the file:
|
||||||
nano ~/.reticulum/config
|
nano ~/.reticulum/config
|
||||||
|
|
||||||
# ... and launch the example again.
|
# ... and launch the example again.
|
||||||
python3 Examples/Echo.py -s
|
python Examples/Echo.py -s
|
||||||
|
|
||||||
# You can now repeat the process on another computer,
|
# You can now repeat the process on another computer,
|
||||||
# and run the same example with -h to get command line options.
|
# and run the same example with -h to get command line options.
|
||||||
python3 Examples/Echo.py -h
|
python Examples/Echo.py -h
|
||||||
|
|
||||||
# Run the example in client mode to "ping" the server.
|
# Run the example in client mode to "ping" the server.
|
||||||
# Replace the hash below with the actual destination hash of your server.
|
# Replace the hash below with the actual destination hash of your server.
|
||||||
python3 Examples/Echo.py 174a64852a75682259ad8b921b8bf416
|
python Examples/Echo.py 174a64852a75682259ad8b921b8bf416
|
||||||
|
|
||||||
# Have a look at another example
|
# Have a look at another example
|
||||||
python3 Examples/Filetransfer.py -h
|
python Examples/Filetransfer.py -h
|
||||||
|
|
||||||
When you have experimented with the basic examples, it's time to go read the
|
When you have experimented with the basic examples, it's time to go read the
|
||||||
:ref:`Understanding Reticulum<understanding-main>` chapter. Before submitting
|
:ref:`Understanding Reticulum<understanding-main>` chapter. Before submitting
|
||||||
@@ -446,11 +447,11 @@ detailed in this manual.
|
|||||||
|
|
||||||
Debian Bookworm
|
Debian Bookworm
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
On versions of Debian released after April 2023, it is no longer possible
|
On versions of Debian released after April 2023, it is no longer possible by default
|
||||||
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
||||||
use the replacement ``pipx`` command instead, which places installed packages in an
|
use the replacement ``pipx`` command instead, which places installed packages in an
|
||||||
isolated environment. This should not negatively affect Reticulum, but installation
|
isolated environment. This should not negatively affect Reticulum, but will not work
|
||||||
via this method is not fully tested yet.
|
for including and using Reticulum in your own scripts and programs.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
@@ -463,14 +464,28 @@ via this method is not fully tested yet.
|
|||||||
# Install Reticulum
|
# Install Reticulum
|
||||||
pipx install rns
|
pipx install rns
|
||||||
|
|
||||||
|
Alternatively, you can restore normal behaviour to ``pip`` by creating or editing
|
||||||
|
the configuration file located at ``~/.config/pip/pip.conf``, and adding the
|
||||||
|
following section:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
[global]
|
||||||
|
break-system-packages = true
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
Ubuntu Lunar
|
Ubuntu Lunar
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
On versions of Ubuntu released after April 2023, it is no longer possible
|
On versions of Ubuntu released after April 2023, it is no longer possible by default
|
||||||
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
to use ``pip`` to install packages onto your system. Unfortunately, you will need to
|
||||||
use the replacement ``pipx`` command instead, which places installed packages in an
|
use the replacement ``pipx`` command instead, which places installed packages in an
|
||||||
isolated environment. This should not negatively affect Reticulum, but installation
|
isolated environment. This should not negatively affect Reticulum, but will not work
|
||||||
via this method is not fully tested yet.
|
for including and using Reticulum in your own scripts and programs.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
@@ -483,14 +498,24 @@ via this method is not fully tested yet.
|
|||||||
# Install Reticulum
|
# Install Reticulum
|
||||||
pipx install rns
|
pipx install rns
|
||||||
|
|
||||||
|
Alternatively, you can restore normal behaviour to ``pip`` by creating or editing
|
||||||
|
the configuration file located at ``~/.config/pip/pip.conf``, and adding the
|
||||||
|
following section:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
[global]
|
||||||
|
break-system-packages = true
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
Pure-Python Reticulum
|
Pure-Python Reticulum
|
||||||
==============================================
|
==============================================
|
||||||
In some rare cases, and on more obscure system types, it is not possible to
|
In some rare cases, and on more obscure system types, it is not possible to
|
||||||
install one or more dependencies
|
install one or more dependencies. In such situations,
|
||||||
|
|
||||||
On more unusual systems, and in some rare cases, it might not be possible to
|
|
||||||
install or even compile one or more of the above modules. In such situations,
|
|
||||||
you can use the ``rnspure`` package instead of the ``rns`` package, or use ``pip``
|
you can use the ``rnspure`` package instead of the ``rns`` package, or use ``pip``
|
||||||
with the ``--no-dependencies`` command-line option. The ``rnspure``
|
with the ``--no-dependencies`` command-line option. The ``rnspure``
|
||||||
package requires no external dependencies for installation. Please note that the
|
package requires no external dependencies for installation. Please note that the
|
||||||
|
|||||||
+40
-18
@@ -24,11 +24,20 @@ starting from scratch.
|
|||||||
This chapter will outline a few different sensible starting paths to get
|
This chapter will outline a few different sensible starting paths to get
|
||||||
real-world functional wireless communications up and running with minimal cost
|
real-world functional wireless communications up and running with minimal cost
|
||||||
and effort. Two fundamental devices categories will be covered, *RNodes* and
|
and effort. Two fundamental devices categories will be covered, *RNodes* and
|
||||||
*WiFi-based radios*.
|
*WiFi-based radios*. Additionally, other common options will be briefly described.
|
||||||
|
|
||||||
|
Knowing how to employ just a few different types of hardware will make it possible
|
||||||
|
to build a wide range of useful networks with little effort.
|
||||||
|
|
||||||
|
Combining Hardware Types
|
||||||
|
========================
|
||||||
|
|
||||||
|
It is useful to combine different link and hardware types when designing and
|
||||||
|
building a network. One useful design pattern is to employ high-capacity point-to-point
|
||||||
|
links based on WiFi or millimeter-wave radios (with high-gain directional antennas)
|
||||||
|
for the network backbone, and using LoRa-based RNodes for covering large areas with
|
||||||
|
connectivity for client devices.
|
||||||
|
|
||||||
While there are many other device categories that are useful in building Reticulum
|
|
||||||
networks, knowing how to employ just these two will make it possible to build
|
|
||||||
a wide range of useful networks with little effort.
|
|
||||||
|
|
||||||
.. _rnode-main:
|
.. _rnode-main:
|
||||||
|
|
||||||
@@ -190,13 +199,6 @@ such as serial port and on-air parameters. For v2.x firmwares, you just need to
|
|||||||
the Connection ID of the RNode, and Reticulum will automatically locate and connect to the
|
the Connection ID of the RNode, and Reticulum will automatically locate and connect to the
|
||||||
RNode, using the parameters stored in the RNode itself.
|
RNode, using the parameters stored in the RNode itself.
|
||||||
|
|
||||||
.. _rnode-suppliers:
|
|
||||||
|
|
||||||
Suppliers
|
|
||||||
^^^^^^^^^
|
|
||||||
Get in touch if you want to have your RNode supplier listed here, or if you want help to
|
|
||||||
get started with producing RNodes.
|
|
||||||
|
|
||||||
|
|
||||||
WiFi-based Hardware
|
WiFi-based Hardware
|
||||||
===================
|
===================
|
||||||
@@ -231,11 +233,31 @@ that is relatively cheap while providing long range and high capacity for Reticu
|
|||||||
networks. As in all other cases, it is also possible for Reticulum to co-exist with IP
|
networks. As in all other cases, it is also possible for Reticulum to co-exist with IP
|
||||||
networks running concurrently on such devices.
|
networks running concurrently on such devices.
|
||||||
|
|
||||||
Combining Hardware Types
|
Ethernet-based Hardware
|
||||||
========================
|
=======================
|
||||||
|
|
||||||
It is useful to combine different link and hardware types when designing and
|
Reticulum can run over any kind of hardware that can provide a switched Ethernet-based
|
||||||
building a network. One useful design pattern is to employ high-capacity point-to-point
|
medium. This means that anything from a plain Ethernet switch, to fiber-optic systems,
|
||||||
links based on WiFi or millimeter-wave radios (with high-gain directional antennas)
|
to data radios with Ethernet interfaces can be used by Reticulum.
|
||||||
for the network backbone, and using LoRa-based RNodes for covering large areas with
|
|
||||||
connectivity for client devices.
|
The Ethernet medium does not need to have any IP infrastructure such as DHCP servers
|
||||||
|
or routing set up, but in case such infrastructure does exist, Reticulum will simply
|
||||||
|
co-exist with.
|
||||||
|
|
||||||
|
To use Reticulum over Ethernet-based mediums, it is generally enough to use the included
|
||||||
|
:ref:`AutoInterface<interfaces-auto>`. This interface also works over any kind of
|
||||||
|
virtual networking adapter, such as ``tun`` and ``tap`` devices in Linux.
|
||||||
|
|
||||||
|
Serial Lines & Devices
|
||||||
|
======================
|
||||||
|
|
||||||
|
Using Reticulum over any kind of raw serial line is also possible with the
|
||||||
|
:ref:`SerialInterface<interfaces-serial>`. This interface type is also useful for
|
||||||
|
using Reticulum over communications hardware that provides a serial port interface.
|
||||||
|
|
||||||
|
Packet Radio Modems
|
||||||
|
===================
|
||||||
|
|
||||||
|
Any packet radio modem that provides a standard KISS interface over USB, serial or TCP
|
||||||
|
can be used with Reticulum. This includes virtual software modems such as
|
||||||
|
`FreeDV TNC <https://github.com/xssfox/freedv-tnc>`_ and `Dire Wolf <https://github.com/wb2osz/direwolf>`_.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
.. _interfaces-main:
|
.. _interfaces-main:
|
||||||
|
|
||||||
********************
|
**********************
|
||||||
Supported Interfaces
|
Configuring Interfaces
|
||||||
********************
|
**********************
|
||||||
|
|
||||||
Reticulum supports using many kinds of devices as networking interfaces, and
|
Reticulum supports using many kinds of devices as networking interfaces, and
|
||||||
allows you to mix and match them in any way you choose. The number of distinct
|
allows you to mix and match them in any way you choose. The number of distinct
|
||||||
@@ -365,6 +365,7 @@ can be used, and offers full control over LoRa parameters.
|
|||||||
# out identification on the channel with
|
# out identification on the channel with
|
||||||
# a set interval by configuring the
|
# a set interval by configuring the
|
||||||
# following two parameters.
|
# following two parameters.
|
||||||
|
|
||||||
# id_callsign = MYCALL-0
|
# id_callsign = MYCALL-0
|
||||||
# id_interval = 600
|
# id_interval = 600
|
||||||
|
|
||||||
@@ -372,7 +373,21 @@ can be used, and offers full control over LoRa parameters.
|
|||||||
# with low amounts of RAM, using packet
|
# with low amounts of RAM, using packet
|
||||||
# flow control can be useful. By default
|
# flow control can be useful. By default
|
||||||
# it is disabled.
|
# it is disabled.
|
||||||
flow_control = False
|
|
||||||
|
# flow_control = False
|
||||||
|
|
||||||
|
# It is possible to limit the airtime
|
||||||
|
# utilisation of an RNode by using the
|
||||||
|
# following two configuration options.
|
||||||
|
# The short-term limit is applied in a
|
||||||
|
# window of approximately 15 seconds,
|
||||||
|
# and the long-term limit is enforced
|
||||||
|
# over a rolling 60 minute window. Both
|
||||||
|
# options are specified in percent.
|
||||||
|
|
||||||
|
# airtime_limit_long = 1.5
|
||||||
|
# airtime_limit_short = 33
|
||||||
|
|
||||||
|
|
||||||
.. _interfaces-serial:
|
.. _interfaces-serial:
|
||||||
|
|
||||||
@@ -746,3 +761,74 @@ conserve bandwidth, while very fast networks can support applications that
|
|||||||
need very frequent announces. Reticulum implements these mechanisms to ensure
|
need very frequent announces. Reticulum implements these mechanisms to ensure
|
||||||
that a large span of network types can seamlessly *co-exist* and interconnect.
|
that a large span of network types can seamlessly *co-exist* and interconnect.
|
||||||
|
|
||||||
|
.. _interfaces-ingress-control:
|
||||||
|
|
||||||
|
New Destination Rate Limiting
|
||||||
|
=============================
|
||||||
|
|
||||||
|
On public interfaces, where anyone may connect and announce new destinations,
|
||||||
|
it can be useful to control the rate at which announces for *new* destinations are
|
||||||
|
processed.
|
||||||
|
|
||||||
|
If a large influx of announces for newly created or previously unknown destinations
|
||||||
|
occur within a short amount of time, Reticulum will place these announces on hold,
|
||||||
|
so that announce traffic for known and previously established destinations can
|
||||||
|
continue to be processed without interruptions.
|
||||||
|
|
||||||
|
After the burst subsides, and an additional waiting period has passed, the held
|
||||||
|
announces will be released at a slow rate, until the hold queue is cleared. This
|
||||||
|
also means, that should a node decide to connect to a public interface, announce
|
||||||
|
a large amount of bogus destinations, and then disconnect, these destination will
|
||||||
|
never make it into path tables and waste network bandwidth on retransmitted
|
||||||
|
announces.
|
||||||
|
|
||||||
|
**It's important to note** that the ingress control works at the level of *individual
|
||||||
|
sub-interfaces*. As an example, this means that one client on a :ref:`TCP Server Interface<interfaces-tcps>`
|
||||||
|
cannot disrupt processing of incoming announces for other connected clients on the same
|
||||||
|
:ref:`TCP Server Interface<interfaces-tcps>`. All other clients on the same interface will still have new announces
|
||||||
|
processed without interruption.
|
||||||
|
|
||||||
|
By default, Reticulum will handle this automatically, and ingress announce
|
||||||
|
control will be enabled on interface where it is sensible to do so. It should
|
||||||
|
generally not be neccessary to modify the ingress control configuration,
|
||||||
|
but all the parameters are exposed for configuration if needed.
|
||||||
|
|
||||||
|
* | The ``ingress_control`` option tells Reticulum whether or not
|
||||||
|
to enable announce ingress control on the interface. Defaults to
|
||||||
|
``True``.
|
||||||
|
|
||||||
|
* | The ``ic_new_time`` option configures how long (in seconds) an
|
||||||
|
interface is considered newly spawned. Defaults to ``2*60*60`` seconds. This
|
||||||
|
option is useful on publicly accessible interfaces that spawn new
|
||||||
|
sub-interfaces when a new client connects.
|
||||||
|
|
||||||
|
* | The ``ic_burst_freq_new`` option sets the maximum announce ingress
|
||||||
|
frequency for newly spawned interfaces. Defaults to ``3.5``
|
||||||
|
announces per second.
|
||||||
|
|
||||||
|
* | The ``ic_burst_freq`` option sets the maximum announce ingress
|
||||||
|
frequency for other interfaces. Defaults to ``12`` announces
|
||||||
|
per second.
|
||||||
|
|
||||||
|
*If an interface exceeds its burst frequency, incoming announces
|
||||||
|
for unknown destinations will be temporarily held in a queue, and
|
||||||
|
not processed until later.*
|
||||||
|
|
||||||
|
* | The ``ic_max_held_announces`` option sets the maximum amount of
|
||||||
|
unique announces that will be held in the queue. Any additional
|
||||||
|
unique announces will be dropped. Defaults to ``256`` announces.
|
||||||
|
|
||||||
|
* | The ``ic_burst_hold`` option sets how much time (in seconds) must
|
||||||
|
pass after the burst frequency drops below its threshold, for the
|
||||||
|
announce burst to be considered cleared. Defaults to ``60``
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
* | The ``ic_burst_penalty`` option sets how much time (in seconds) must
|
||||||
|
pass after the burst is considered cleared, before held announces can
|
||||||
|
start being released from the queue. Defaults to ``5*60``
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
* | The ``ic_held_release_interval`` option sets how much time (in seconds)
|
||||||
|
must pass between releasing each held announce from the queue. Defaults
|
||||||
|
to ``30`` seconds.
|
||||||
|
|
||||||
|
|||||||
@@ -858,7 +858,7 @@ both on general-purpose CPUs and on microcontrollers. The necessary primitives a
|
|||||||
|
|
||||||
* Ed25519 for signatures
|
* Ed25519 for signatures
|
||||||
|
|
||||||
* X22519 for ECDH key exchanges
|
* X25519 for ECDH key exchanges
|
||||||
|
|
||||||
* HKDF for key derivation
|
* HKDF for key derivation
|
||||||
|
|
||||||
|
|||||||
+330
-69
@@ -99,6 +99,17 @@ configuration file is created. The default configuration looks like this:
|
|||||||
instance_control_port = 37429
|
instance_control_port = 37429
|
||||||
|
|
||||||
|
|
||||||
|
# On systems where running instances may not have access
|
||||||
|
# to the same shared Reticulum configuration directory,
|
||||||
|
# it is still possible to allow full interactivity for
|
||||||
|
# running instances, by manually specifying a shared RPC
|
||||||
|
# key. In almost all cases, this option is not needed, but
|
||||||
|
# it can be useful on operating systems such as Android.
|
||||||
|
# The key must be specified as bytes in hexadecimal.
|
||||||
|
|
||||||
|
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
|
||||||
|
|
||||||
|
|
||||||
# You can configure Reticulum to panic and forcibly close
|
# You can configure Reticulum to panic and forcibly close
|
||||||
# if an unrecoverable interface error occurs, such as the
|
# if an unrecoverable interface error occurs, such as the
|
||||||
# hardware device for an interface disappearing. This is
|
# hardware device for an interface disappearing. This is
|
||||||
@@ -108,6 +119,17 @@ configuration file is created. The default configuration looks like this:
|
|||||||
panic_on_interface_error = No
|
panic_on_interface_error = No
|
||||||
|
|
||||||
|
|
||||||
|
# When Transport is enabled, it is possible to allow the
|
||||||
|
# Transport Instance to respond to probe requests from
|
||||||
|
# the rnprobe utility. This can be a useful tool to test
|
||||||
|
# connectivity. When this option is enabled, the probe
|
||||||
|
# destination will be generated from the Identity of the
|
||||||
|
# Transport Instance, and printed to the log at startup.
|
||||||
|
# Optional, and disabled by default.
|
||||||
|
|
||||||
|
respond_to_probes = No
|
||||||
|
|
||||||
|
|
||||||
[logging]
|
[logging]
|
||||||
# Valid log levels are 0 through 7:
|
# Valid log levels are 0 through 7:
|
||||||
# 0: Log only critical information
|
# 0: Log only critical information
|
||||||
@@ -145,10 +167,19 @@ configuration file is created. The default configuration looks like this:
|
|||||||
If Reticulum infrastructure already exists locally, you probably don't need to
|
If Reticulum infrastructure already exists locally, you probably don't need to
|
||||||
change anything, and you may already be connected to a wider network. If not,
|
change anything, and you may already be connected to a wider network. If not,
|
||||||
you will probably need to add relevant *interfaces* to the configuration, in
|
you will probably need to add relevant *interfaces* to the configuration, in
|
||||||
order to communicate with other systems. It is a good idea to read the comments
|
order to communicate with other systems.
|
||||||
and explanations in the above default config. It will teach you the basic
|
|
||||||
concepts you need to understand to configure your network. Once you have done that,
|
You can generate a much more verbose configuration example by running the command:
|
||||||
take a look at the :ref:`Interfaces<interfaces-main>` chapter of this manual.
|
|
||||||
|
``rnsd --exampleconfig``
|
||||||
|
|
||||||
|
The output includes examples for most interface types supported
|
||||||
|
by Reticulum, along with additional options and configuration parameters.
|
||||||
|
|
||||||
|
It is a good idea to read the comments and explanations in the above default config.
|
||||||
|
It will teach you the basic concepts you need to understand to configure your network.
|
||||||
|
Once you have done that, take a look at the :ref:`Interfaces<interfaces-main>` chapter
|
||||||
|
of this manual.
|
||||||
|
|
||||||
Included Utility Programs
|
Included Utility Programs
|
||||||
-------------------------
|
-------------------------
|
||||||
@@ -170,28 +201,47 @@ When ``rnsd`` is running, it will keep all configured interfaces open, handle tr
|
|||||||
it is enabled, and allow any other programs to immediately utilise the
|
it is enabled, and allow any other programs to immediately utilise the
|
||||||
Reticulum network it is configured for.
|
Reticulum network it is configured for.
|
||||||
|
|
||||||
You can even run multiple instances of rnsd with different configurations on
|
You can even run multiple instances of ``rnsd`` with different configurations on
|
||||||
the same system.
|
the same system.
|
||||||
|
|
||||||
.. code:: text
|
**Usage Examples**
|
||||||
|
|
||||||
# Install Reticulum
|
Run ``rnsd``:
|
||||||
pip3 install rns
|
|
||||||
|
|
||||||
# Run rnsd
|
|
||||||
rnsd
|
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnsd [-h] [--config CONFIG] [-v] [-q] [--version]
|
$ rnsd
|
||||||
|
|
||||||
|
[2023-08-18 17:59:56] [Notice] Started rnsd version 0.5.8
|
||||||
|
|
||||||
|
Run ``rnsd`` in service mode, ensuring all logging output is sent directly to file:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnsd -s
|
||||||
|
|
||||||
|
Generate a verbose and detailed configuration example, with explanations of all the
|
||||||
|
various configuration options, and interface configuration examples:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnsd --exampleconfig
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnsd.py [-h] [--config CONFIG] [-v] [-q] [-s] [--exampleconfig] [--version]
|
||||||
|
|
||||||
Reticulum Network Stack Daemon
|
Reticulum Network Stack Daemon
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
-q, --quiet
|
-q, --quiet
|
||||||
|
-s, --service rnsd is running as a service and should log to file
|
||||||
|
--exampleconfig print verbose configuration example to stdout and exit
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
|
|
||||||
You can easily add ``rnsd`` as an always-on service by :ref:`configuring a service<using-systemd>`.
|
You can easily add ``rnsd`` as an always-on service by :ref:`configuring a service<using-systemd>`.
|
||||||
@@ -202,12 +252,14 @@ The rnstatus Utility
|
|||||||
Using the ``rnstatus`` utility, you can view the status of configured Reticulum
|
Using the ``rnstatus`` utility, you can view the status of configured Reticulum
|
||||||
interfaces, similar to the ``ifconfig`` program.
|
interfaces, similar to the ``ifconfig`` program.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Run ``rnstatus``:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnstatus
|
$ rnstatus
|
||||||
rnstatus
|
|
||||||
|
|
||||||
# Example output
|
|
||||||
Shared Instance[37428]
|
Shared Instance[37428]
|
||||||
Status : Up
|
Status : Up
|
||||||
Serving : 1 program
|
Serving : 1 program
|
||||||
@@ -240,44 +292,175 @@ interfaces, similar to the ``ifconfig`` program.
|
|||||||
|
|
||||||
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||||
|
|
||||||
|
Filter output to only show some interfaces:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnstatus [-h] [--config CONFIG] [--version] [-a] [-v]
|
$ rnstatus rnode
|
||||||
|
|
||||||
|
RNodeInterface[RNode UHF]
|
||||||
|
Status : Up
|
||||||
|
Mode : Access Point
|
||||||
|
Rate : 1.30 kbps
|
||||||
|
Access : 64-bit IFAC by <…e702c42ba8>
|
||||||
|
Traffic : 8.49 KB↑
|
||||||
|
9.23 KB↓
|
||||||
|
|
||||||
|
Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
|
||||||
|
[-r] [-j] [-v] [filter]
|
||||||
|
|
||||||
Reticulum Network Stack Status
|
Reticulum Network Stack Status
|
||||||
|
|
||||||
optional arguments:
|
positional arguments:
|
||||||
-h, --help show this help message and exit
|
filter only display interfaces with names including filter
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
|
||||||
--version show program's version number and exit
|
options:
|
||||||
-a, --all show all interfaces
|
-h, --help show this help message and exit
|
||||||
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
|
--version show program's version number and exit
|
||||||
|
-a, --all show all interfaces
|
||||||
|
-A, --announce-stats show announce stats
|
||||||
|
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
|
||||||
|
-r, --reverse reverse sorting
|
||||||
|
-j, --json output in JSON format
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
|
|
||||||
|
|
||||||
|
The rnid Utility
|
||||||
|
====================
|
||||||
|
|
||||||
|
With the ``rnid`` utility, you can generate, manage and view Reticulum Identities.
|
||||||
|
The program can also calculate Destination hashes, and perform encryption and
|
||||||
|
decryption of files.
|
||||||
|
|
||||||
|
Using ``rnid``, it is possible to asymmetrically encrypt files and information for
|
||||||
|
any Reticulum destination hash, and also to create and verify cryptographic signatures.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Generate a new Identity:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -g ./new_identity
|
||||||
|
|
||||||
|
Display Identity key information:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -i ./new_identity -p
|
||||||
|
|
||||||
|
Loaded Identity <984b74a3f768bef236af4371e6f248cd> from new_id
|
||||||
|
Public Key : 0f4259fef4521ab75a3409e353fe9073eb10783b4912a6a9937c57bf44a62c1e
|
||||||
|
Private Key : Hidden
|
||||||
|
|
||||||
|
Encrypt a file for an LXMF user:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -i 8dd57a738226809646089335a6b03695 -e my_file.txt
|
||||||
|
|
||||||
|
Recalled Identity <bc7291552be7a58f361522990465165c> for destination <8dd57a738226809646089335a6b03695>
|
||||||
|
Encrypting my_file.txt
|
||||||
|
File my_file.txt encrypted for <bc7291552be7a58f361522990465165c> to my_file.txt.rfe
|
||||||
|
|
||||||
|
If the Identity for the destination is not already known, you can fetch it from the network by using the ``-R`` command-line option:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -R -i 30602def3b3506a28ed33db6f60cc6c9 -e my_file.txt
|
||||||
|
|
||||||
|
Requesting unknown Identity for <30602def3b3506a28ed33db6f60cc6c9>...
|
||||||
|
Received Identity <2b489d06eaf7c543808c76a5332a447d> for destination <30602def3b3506a28ed33db6f60cc6c9> from the network
|
||||||
|
Encrypting my_file.txt
|
||||||
|
File my_file.txt encrypted for <2b489d06eaf7c543808c76a5332a447d> to my_file.txt.rfe
|
||||||
|
|
||||||
|
Decrypt a file using the Reticulum Identity it was encrypted for:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnid -i ./my_identity -d my_file.txt.rfe
|
||||||
|
|
||||||
|
Loaded Identity <2225fdeecaf6e2db4556c3c2d7637294> from ./my_identity
|
||||||
|
Decrypting ./my_file.txt.rfe...
|
||||||
|
File ./my_file.txt.rfe decrypted with <2225fdeecaf6e2db4556c3c2d7637294> to ./my_file.txt
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnid.py [-h] [--config path] [-i identity] [-g path] [-v] [-q] [-a aspects]
|
||||||
|
[-H aspects] [-e path] [-d path] [-s path] [-V path] [-r path] [-w path]
|
||||||
|
[-f] [-R] [-t seconds] [-p] [-P] [--version]
|
||||||
|
|
||||||
|
Reticulum Identity & Encryption Utility
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--config path path to alternative Reticulum config directory
|
||||||
|
-i identity, --identity identity
|
||||||
|
hexadecimal Reticulum Destination hash or path to Identity file
|
||||||
|
-g path, --generate path
|
||||||
|
generate a new Identity
|
||||||
|
-v, --verbose increase verbosity
|
||||||
|
-q, --quiet decrease verbosity
|
||||||
|
-a aspects, --announce aspects
|
||||||
|
announce a destination based on this Identity
|
||||||
|
-H aspects, --hash aspects
|
||||||
|
show destination hashes for other aspects for this Identity
|
||||||
|
-e path, --encrypt path
|
||||||
|
encrypt file
|
||||||
|
-d path, --decrypt path
|
||||||
|
decrypt file
|
||||||
|
-s path, --sign path sign file
|
||||||
|
-V path, --validate path
|
||||||
|
validate signature
|
||||||
|
-r path, --read path input file path
|
||||||
|
-w path, --write path
|
||||||
|
output file path
|
||||||
|
-f, --force write output even if it overwrites existing files
|
||||||
|
-R, --request request unknown Identities from the network
|
||||||
|
-t seconds identity request timeout before giving up
|
||||||
|
-p, --print-identity print identity info and exit
|
||||||
|
-P, --print-private allow displaying private keys
|
||||||
|
--version show program's version number and exit
|
||||||
|
|
||||||
|
|
||||||
The rnpath Utility
|
The rnpath Utility
|
||||||
====================
|
====================
|
||||||
|
|
||||||
With the ``rnpath`` utility, you can look up and view paths for
|
With the ``rnpath`` utility, you can look up and view paths for
|
||||||
destinations on the Reticulum network.
|
destinations on the Reticulum network.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Resolve path to a destination:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnpath
|
$ rnpath c89b4da064bf66d280f0e4d8abfd9806
|
||||||
rnpath c89b4da064bf66d280f0e4d8abfd9806
|
|
||||||
|
|
||||||
# Example output
|
|
||||||
Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away via <f53a1c4278e0726bb73fcc623d6ce763> on TCPInterface[Testnet/dublin.connect.reticulum.network:4965]
|
Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away via <f53a1c4278e0726bb73fcc623d6ce763> on TCPInterface[Testnet/dublin.connect.reticulum.network:4965]
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D] [-w seconds] [-v] [destination]
|
usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
|
||||||
|
[-x] [-w seconds] [-v] [destination]
|
||||||
|
|
||||||
Reticulum Path Discovery Utility
|
Reticulum Path Discovery Utility
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
destination hexadecimal hash of the destination
|
destination hexadecimal hash of the destination
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
@@ -285,6 +468,7 @@ destinations on the Reticulum network.
|
|||||||
-r, --rates show announce rate info
|
-r, --rates show announce rate info
|
||||||
-d, --drop remove the path to a destination
|
-d, --drop remove the path to a destination
|
||||||
-D, --drop-announces drop all queued announces
|
-D, --drop-announces drop all queued announces
|
||||||
|
-x, --drop-via drop all paths via specified transport instance
|
||||||
-w seconds timeout before giving up
|
-w seconds timeout before giving up
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
|
|
||||||
@@ -295,21 +479,53 @@ The rnprobe Utility
|
|||||||
The ``rnprobe`` utility lets you probe a destination for connectivity, similar
|
The ``rnprobe`` utility lets you probe a destination for connectivity, similar
|
||||||
to the ``ping`` program. Please note that probes will only be answered if the
|
to the ``ping`` program. Please note that probes will only be answered if the
|
||||||
specified destination is configured to send proofs for received packets. Many
|
specified destination is configured to send proofs for received packets. Many
|
||||||
destinations will not have this option enabled, and will not be probable.
|
destinations will not have this option enabled, so most destinations will not
|
||||||
|
be probable.
|
||||||
|
|
||||||
|
You can enable a probe-reply destination on Reticulum Transport Instances by
|
||||||
|
setting the ``respond_to_probes`` configuration directive. Reticulum will then
|
||||||
|
print the probe destination to the log on Transport Instance startup.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Probe a destination:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnprobe
|
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708
|
||||||
rnprobe example_utilities.echo.request 2d03725b327348980d570f739a3a5708
|
|
||||||
|
|
||||||
# Example output
|
|
||||||
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
||||||
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
||||||
Round-trip time is 38.469 milliseconds over 2 hops
|
Round-trip time is 38.469 milliseconds over 2 hops
|
||||||
|
|
||||||
|
Send a larger probe:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708 -s 256
|
||||||
|
|
||||||
|
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
|
||||||
|
Valid reply received from <2d03725b327348980d570f739a3a5708>
|
||||||
|
Round-trip time is 38.781 milliseconds over 2 hops
|
||||||
|
|
||||||
|
If the interface that receives the probe replies supports reporting radio
|
||||||
|
parameters such as **RSSI** and **SNR**, the ``rnprobe`` utility will print
|
||||||
|
these as part of the result as well.
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnprobe rnstransport.probe e7536ee90bd4a440e130490b87a25124
|
||||||
|
|
||||||
|
Sent 16 byte probe to <e7536ee90bd4a440e130490b87a25124>
|
||||||
|
Valid reply received from <e7536ee90bd4a440e130490b87a25124>
|
||||||
|
Round-trip time is 1.809 seconds over 1 hop [RSSI -73 dBm] [SNR 12.0 dB]
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [-s SIZE]
|
||||||
|
[full_name] [destination_hash]
|
||||||
|
|
||||||
Reticulum Probe Utility
|
Reticulum Probe Utility
|
||||||
|
|
||||||
@@ -320,6 +536,7 @@ destinations will not have this option enabled, and will not be probable.
|
|||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config CONFIG path to alternative Reticulum config directory
|
--config CONFIG path to alternative Reticulum config directory
|
||||||
|
-s SIZE, --size SIZE size of probe packet payload in bytes
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
|
|
||||||
@@ -330,20 +547,39 @@ The rncp Utility
|
|||||||
The ``rncp`` utility is a simple file transfer tool. Using it, you can transfer
|
The ``rncp`` utility is a simple file transfer tool. Using it, you can transfer
|
||||||
files through Reticulum.
|
files through Reticulum.
|
||||||
|
|
||||||
.. code:: text
|
**Usage Examples**
|
||||||
|
|
||||||
# Run rncp on the receiving system, specifying which identities
|
Run rncp on the receiving system, specifying which identities are allowed to send files:
|
||||||
# are allowed to send files
|
|
||||||
rncp --receive -a 1726dbad538775b5bf9b0ea25a4079c8 -a c50cc4e4f7838b6c31f60ab9032cbc62
|
|
||||||
|
|
||||||
# From another system, copy a file to the receiving system
|
|
||||||
rncp ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
|
||||||
|
|
||||||
You can specify as many allowed senders as needed, or complete disable authentication.
|
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rncp [-h] [--config path] [-v] [-q] [-p] [-r] [-b] [-a allowed_hash] [-n] [-w seconds] [--version] [file] [destination]
|
$ rncp --listen -a 1726dbad538775b5bf9b0ea25a4079c8 -a c50cc4e4f7838b6c31f60ab9032cbc62
|
||||||
|
|
||||||
|
You can also specify allowed identity hashes (one per line) in the file ~/.rncp/allowed_identities
|
||||||
|
and simply running the program in listener mode:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rncp --listen
|
||||||
|
|
||||||
|
From another system, copy a file to the receiving system:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rncp ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
||||||
|
|
||||||
|
Or fetch a file from the remote system:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rncp --fetch ~/path/to/file.tgz 73cbd378bb0286ed11a707c13447bb1e
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rncp.py [-h] [--config path] [-v] [-q] [-S] [-l] [-f] [-b seconds]
|
||||||
|
[-a allowed_hash] [-n] [-p] [-w seconds] [--version] [file] [destination]
|
||||||
|
|
||||||
Reticulum File Transfer Utility
|
Reticulum File Transfer Utility
|
||||||
|
|
||||||
@@ -351,19 +587,20 @@ You can specify as many allowed senders as needed, or complete disable authentic
|
|||||||
file file to be transferred
|
file file to be transferred
|
||||||
destination hexadecimal hash of the receiver
|
destination hexadecimal hash of the receiver
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config path path to alternative Reticulum config directory
|
--config path path to alternative Reticulum config directory
|
||||||
-v, --verbose increase verbosity
|
-v, --verbose increase verbosity
|
||||||
-q, --quiet decrease verbosity
|
-q, --quiet decrease verbosity
|
||||||
-p, --print-identity print identity and destination info and exit
|
-S, --silent disable transfer progress output
|
||||||
-r, --receive wait for incoming files
|
-l, --listen listen for incoming transfer requests
|
||||||
-b, --no-announce don't announce at program start
|
-f, --fetch fetch file from remote listener instead of sending
|
||||||
|
-b seconds announce interval, 0 to only announce at startup
|
||||||
-a allowed_hash accept from this identity
|
-a allowed_hash accept from this identity
|
||||||
-n, --no-auth accept files from anyone
|
-n, --no-auth accept files and fetches from anyone
|
||||||
|
-p, --print-identity print identity and destination info and exit
|
||||||
-w seconds sender timeout before giving up
|
-w seconds sender timeout before giving up
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-v, --verbose
|
|
||||||
|
|
||||||
|
|
||||||
The rnx Utility
|
The rnx Utility
|
||||||
@@ -371,32 +608,43 @@ The rnx Utility
|
|||||||
|
|
||||||
The ``rnx`` utility is a basic remote command execution program. It allows you to
|
The ``rnx`` utility is a basic remote command execution program. It allows you to
|
||||||
execute commands on remote systems over Reticulum, and to view returned command
|
execute commands on remote systems over Reticulum, and to view returned command
|
||||||
output.
|
output. For a fully interactive remote shell solution, be sure to also take a look
|
||||||
|
at the `rnsh <https://github.com/acehoss/rnsh>`_ program.
|
||||||
|
|
||||||
|
**Usage Examples**
|
||||||
|
|
||||||
|
Run rnx on the listening system, specifying which identities are allowed to execute commands:
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
# Run rnx on the listening system, specifying which identities
|
$ rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||||
# are allowed to execute commands
|
|
||||||
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
|
||||||
|
|
||||||
# From another system, run a command
|
From another system, run a command on the remote:
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
|
||||||
|
|
||||||
# Or enter the interactive mode pseudo-shell
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 -x
|
|
||||||
|
|
||||||
# The default identity file is stored in
|
|
||||||
# ~/.reticulum/identities/rnx, but you can use
|
|
||||||
# another one, which will be created if it does
|
|
||||||
# not already exist
|
|
||||||
rnx 7a55144adf826958a9529a3bcf08b149 -i /path/to/identity -x
|
|
||||||
|
|
||||||
You can specify as many allowed senders as needed, or completely disable authentication.
|
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-a allowed_hash] [-n] [-N] [-d] [-m] [-w seconds] [-W seconds] [--stdin STDIN] [--stdout STDOUT] [--stderr STDERR] [--version]
|
$ rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
||||||
[destination] [command]
|
|
||||||
|
Or enter the interactive mode pseudo-shell:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnx 7a55144adf826958a9529a3bcf08b149 -x
|
||||||
|
|
||||||
|
The default identity file is stored in ``~/.reticulum/identities/rnx``, but you can use
|
||||||
|
another one, which will be created if it does not already exist
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
$ rnx 7a55144adf826958a9529a3bcf08b149 -i /path/to/identity -x
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-n] [-N]
|
||||||
|
[-d] [-m] [-a allowed_hash] [-w seconds] [-W seconds] [--stdin STDIN]
|
||||||
|
[--stdout STDOUT] [--stderr STDERR] [--version] [destination] [command]
|
||||||
|
|
||||||
Reticulum Remote Execution Utility
|
Reticulum Remote Execution Utility
|
||||||
|
|
||||||
@@ -433,11 +681,19 @@ The rnodeconf Utility
|
|||||||
The ``rnodeconf`` utility allows you to inspect and configure existing :ref:`RNodes<rnode-main>`, and
|
The ``rnodeconf`` utility allows you to inspect and configure existing :ref:`RNodes<rnode-main>`, and
|
||||||
to create and provision new :ref:`RNodes<rnode-main>` from any supported hardware devices.
|
to create and provision new :ref:`RNodes<rnode-main>` from any supported hardware devices.
|
||||||
|
|
||||||
|
**All Command-Line Options**
|
||||||
|
|
||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
usage: rnodeconf [-h] [-i] [-a] [-u] [-U] [--fw-version version] [--nocheck] [-C] [-N] [-T] [-b] [-B] [-p] [--freq Hz] [--bw Hz] [--txp dBm] [--sf factor] [--cr rate] [--eeprom-backup] [--eeprom-dump] [--eeprom-wipe] [--version] [port]
|
usage: rnodeconf.py [-h] [-i] [-a] [-u] [-U] [--fw-version version] [--nocheck] [-e]
|
||||||
|
[-E] [-C] [--baud-flash baud_flash] [-N] [-T] [-b] [-B] [-p] [-D i]
|
||||||
|
[--freq Hz] [--bw Hz] [--txp dBm] [--sf factor] [--cr rate]
|
||||||
|
[--eeprom-backup] [--eeprom-dump] [--eeprom-wipe] [-P]
|
||||||
|
[--trust-key hexbytes] [--version] [port]
|
||||||
|
|
||||||
RNode Configuration and firmware utility. This program allows you to change various settings and startup modes of RNode. It can also install, flash and update the firmware on supported devices.
|
RNode Configuration and firmware utility. This program allows you to change various
|
||||||
|
settings and startup modes of RNode. It can also install, flash and update the firmware
|
||||||
|
on supported devices.
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
port serial port where RNode is attached
|
port serial port where RNode is attached
|
||||||
@@ -453,11 +709,14 @@ to create and provision new :ref:`RNodes<rnode-main>` from any supported hardwar
|
|||||||
-e, --extract Extract firmware from connected RNode for later use
|
-e, --extract Extract firmware from connected RNode for later use
|
||||||
-E, --use-extracted Use the extracted firmware for autoinstallation or update
|
-E, --use-extracted Use the extracted firmware for autoinstallation or update
|
||||||
-C, --clear-cache Clear locally cached firmware files
|
-C, --clear-cache Clear locally cached firmware files
|
||||||
|
--baud-flash baud_flash
|
||||||
|
Set specific baud rate when flashing device. Default is 921600
|
||||||
-N, --normal Switch device to normal mode
|
-N, --normal Switch device to normal mode
|
||||||
-T, --tnc Switch device to TNC mode
|
-T, --tnc Switch device to TNC mode
|
||||||
-b, --bluetooth-on Turn device bluetooth on
|
-b, --bluetooth-on Turn device bluetooth on
|
||||||
-B, --bluetooth-off Turn device bluetooth off
|
-B, --bluetooth-off Turn device bluetooth off
|
||||||
-p, --bluetooth-pair Put device into bluetooth pairing mode
|
-p, --bluetooth-pair Put device into bluetooth pairing mode
|
||||||
|
-D i, --display i Set display intensity (0-255)
|
||||||
--freq Hz Frequency in Hz for TNC mode
|
--freq Hz Frequency in Hz for TNC mode
|
||||||
--bw Hz Bandwidth in Hz for TNC mode
|
--bw Hz Bandwidth in Hz for TNC mode
|
||||||
--txp dBm TX power in dBm for TNC mode
|
--txp dBm TX power in dBm for TNC mode
|
||||||
@@ -466,6 +725,8 @@ to create and provision new :ref:`RNodes<rnode-main>` from any supported hardwar
|
|||||||
--eeprom-backup Backup EEPROM to file
|
--eeprom-backup Backup EEPROM to file
|
||||||
--eeprom-dump Dump EEPROM to console
|
--eeprom-dump Dump EEPROM to console
|
||||||
--eeprom-wipe Unlock and wipe EEPROM
|
--eeprom-wipe Unlock and wipe EEPROM
|
||||||
|
-P, --public Display public part of signing key
|
||||||
|
--trust-key hexbytes Public key to trust for device verification
|
||||||
--version Print program version and exit
|
--version Print program version and exit
|
||||||
|
|
||||||
For more information on how to create your own RNodes, please read the :ref:`Creating RNodes<rnode-creating>`
|
For more information on how to create your own RNodes, please read the :ref:`Creating RNodes<rnode-creating>`
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ networks, without any need for hierarchical or beaureucratic structures to contr
|
|||||||
or manage them, while ensuring individuals and communities full sovereignty
|
or manage them, while ensuring individuals and communities full sovereignty
|
||||||
over their own network segments.
|
over their own network segments.
|
||||||
|
|
||||||
Reticulum is a complete networking stack, and does not need IP or higher
|
Reticulum is a **complete networking stack**, and does not need IP or higher
|
||||||
layers, although it is easy to utilise IP (with TCP or UDP) as the underlying
|
layers, although it is easy to utilise IP (with TCP or UDP) as the underlying
|
||||||
carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the
|
carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the
|
||||||
Internet or private IP networks. Reticulum is built directly on cryptographic
|
Internet or private IP networks. Reticulum is built directly on cryptographic
|
||||||
principles, allowing resilience and stable functionality in open and trustless
|
principles, allowing resilience and stable functionality in open and trustless
|
||||||
networks.
|
networks.
|
||||||
|
|
||||||
No kernel modules or drivers are required. Reticulum runs completely in
|
No kernel modules or drivers are required. Reticulum can run completely in
|
||||||
userland, and can run on practically any system that runs Python 3. Reticulum
|
userland, and will run on practically any system that runs Python 3. Reticulum
|
||||||
runs well even on small single-board computers like the Pi Zero.
|
runs well even on small single-board computers like the Pi Zero.
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ Current Status
|
|||||||
**Please know!** Reticulum should currently be considered beta software. All core protocol
|
**Please know!** Reticulum should currently be considered beta software. All core protocol
|
||||||
features are implemented and functioning, but additions will probably occur as
|
features are implemented and functioning, but additions will probably occur as
|
||||||
real-world use is explored. *There will be bugs*. The API and wire-format can be
|
real-world use is explored. *There will be bugs*. The API and wire-format can be
|
||||||
considered stable at the moment, but could change if absolutely warranted.
|
considered complete and stable at the moment, but could change if absolutely warranted.
|
||||||
|
|
||||||
|
|
||||||
What does Reticulum Offer?
|
What does Reticulum Offer?
|
||||||
@@ -71,7 +71,7 @@ What does Reticulum Offer?
|
|||||||
|
|
||||||
* Efficient link establishment
|
* Efficient link establishment
|
||||||
|
|
||||||
* Total bandwidth cost of setting up a link is only 3 packets, totalling 297 bytes
|
* Total cost of setting up an encrypted and verified link is only 3 packets, totalling 297 bytes
|
||||||
|
|
||||||
* Low cost of keeping links open at only 0.44 bits per second
|
* Low cost of keeping links open at only 0.44 bits per second
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ setuptools.setup(
|
|||||||
'rnid=RNS.Utilities.rnid:main',
|
'rnid=RNS.Utilities.rnid:main',
|
||||||
'rncp=RNS.Utilities.rncp:main',
|
'rncp=RNS.Utilities.rncp:main',
|
||||||
'rnx=RNS.Utilities.rnx:main',
|
'rnx=RNS.Utilities.rnx:main',
|
||||||
|
'rnir=RNS.Utilities.rnir:main',
|
||||||
'rnodeconf=RNS.Utilities.rnodeconf:main',
|
'rnodeconf=RNS.Utilities.rnodeconf:main',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
+72
-72
@@ -404,104 +404,104 @@ class TestChannel(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(data, decoded)
|
self.assertEqual(data, decoded)
|
||||||
|
|
||||||
# def test_buffer_big(self):
|
def test_buffer_big(self):
|
||||||
# writer = RNS.Buffer.create_writer(15, self.h.channel)
|
writer = RNS.Buffer.create_writer(15, self.h.channel)
|
||||||
# reader = RNS.Buffer.create_reader(15, self.h.channel)
|
reader = RNS.Buffer.create_reader(15, self.h.channel)
|
||||||
# data = "01234556789"*1024 # 10 KB
|
data = "01234556789"*1024*5 # 50 KB
|
||||||
# count = 0
|
count = 0
|
||||||
# write_finished = False
|
write_finished = False
|
||||||
|
|
||||||
# def write_thread():
|
def write_thread():
|
||||||
# nonlocal count, write_finished
|
nonlocal count, write_finished
|
||||||
# count = writer.write(data.encode("utf-8"))
|
count = writer.write(data.encode("utf-8"))
|
||||||
# writer.flush()
|
writer.flush()
|
||||||
# writer.close()
|
writer.close()
|
||||||
# write_finished = True
|
write_finished = True
|
||||||
# threading.Thread(target=write_thread, name="Write Thread", daemon=True).start()
|
threading.Thread(target=write_thread, name="Write Thread", daemon=True).start()
|
||||||
|
|
||||||
# while not write_finished or next(filter(lambda x: x.state != MessageState.MSGSTATE_DELIVERED,
|
while not write_finished or next(filter(lambda x: x.state != MessageState.MSGSTATE_DELIVERED,
|
||||||
# self.h.outlet.packets), None) is not None:
|
self.h.outlet.packets), None) is not None:
|
||||||
# with self.h.outlet.lock:
|
with self.h.outlet.lock:
|
||||||
# for packet in self.h.outlet.packets:
|
for packet in self.h.outlet.packets:
|
||||||
# if packet.state != MessageState.MSGSTATE_DELIVERED:
|
if packet.state != MessageState.MSGSTATE_DELIVERED:
|
||||||
# self.h.channel._receive(packet.raw)
|
self.h.channel._receive(packet.raw)
|
||||||
# packet.delivered()
|
packet.delivered()
|
||||||
# time.sleep(0.0001)
|
time.sleep(0.0001)
|
||||||
|
|
||||||
# self.assertEqual(len(data), count)
|
self.assertEqual(len(data), count)
|
||||||
|
|
||||||
# read_finished = False
|
read_finished = False
|
||||||
# result = bytes()
|
result = bytes()
|
||||||
|
|
||||||
# def read_thread():
|
def read_thread():
|
||||||
# nonlocal read_finished, result
|
nonlocal read_finished, result
|
||||||
# result = reader.read()
|
result = reader.read()
|
||||||
# read_finished = True
|
read_finished = True
|
||||||
# threading.Thread(target=read_thread, name="Read Thread", daemon=True).start()
|
threading.Thread(target=read_thread, name="Read Thread", daemon=True).start()
|
||||||
|
|
||||||
# timeout_at = time.time() + 7
|
timeout_at = time.time() + 7
|
||||||
# while not read_finished and time.time() < timeout_at:
|
while not read_finished and time.time() < timeout_at:
|
||||||
# time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
|
|
||||||
# self.assertTrue(read_finished)
|
self.assertTrue(read_finished)
|
||||||
# self.assertEqual(len(data), len(result))
|
self.assertEqual(len(data), len(result))
|
||||||
|
|
||||||
# decoded = result.decode("utf-8")
|
decoded = result.decode("utf-8")
|
||||||
|
|
||||||
# self.assertSequenceEqual(data, decoded)
|
self.assertSequenceEqual(data, decoded)
|
||||||
|
|
||||||
# def test_buffer_small_with_callback(self):
|
def test_buffer_small_with_callback(self):
|
||||||
# callbacks = 0
|
callbacks = 0
|
||||||
# last_cb_value = None
|
last_cb_value = None
|
||||||
|
|
||||||
# def callback(ready: int):
|
def callback(ready: int):
|
||||||
# nonlocal callbacks, last_cb_value
|
nonlocal callbacks, last_cb_value
|
||||||
# callbacks += 1
|
callbacks += 1
|
||||||
# last_cb_value = ready
|
last_cb_value = ready
|
||||||
|
|
||||||
# data = "Hello\n"
|
data = "Hello\n"
|
||||||
# with RNS.RawChannelWriter(0, self.h.channel) as writer, RNS.RawChannelReader(0, self.h.channel) as reader:
|
with RNS.RawChannelWriter(0, self.h.channel) as writer, RNS.RawChannelReader(0, self.h.channel) as reader:
|
||||||
# reader.add_ready_callback(callback)
|
reader.add_ready_callback(callback)
|
||||||
# count = writer.write(data.encode("utf-8"))
|
count = writer.write(data.encode("utf-8"))
|
||||||
# writer.flush()
|
writer.flush()
|
||||||
|
|
||||||
# self.assertEqual(len(data), count)
|
self.assertEqual(len(data), count)
|
||||||
# self.assertEqual(1, len(self.h.outlet.packets))
|
self.assertEqual(1, len(self.h.outlet.packets))
|
||||||
|
|
||||||
# packet = self.h.outlet.packets[0]
|
packet = self.h.outlet.packets[0]
|
||||||
# self.h.channel._receive(packet.raw)
|
self.h.channel._receive(packet.raw)
|
||||||
# packet.delivered()
|
packet.delivered()
|
||||||
|
|
||||||
# self.assertEqual(1, callbacks)
|
self.assertEqual(1, callbacks)
|
||||||
# self.assertEqual(len(data), last_cb_value)
|
self.assertEqual(len(data), last_cb_value)
|
||||||
|
|
||||||
# result = reader.readline()
|
result = reader.readline()
|
||||||
|
|
||||||
# self.assertIsNotNone(result)
|
self.assertIsNotNone(result)
|
||||||
# self.assertEqual(len(result), len(data))
|
self.assertEqual(len(result), len(data))
|
||||||
|
|
||||||
# decoded = result.decode("utf-8")
|
decoded = result.decode("utf-8")
|
||||||
|
|
||||||
# self.assertEqual(data, decoded)
|
self.assertEqual(data, decoded)
|
||||||
# self.assertEqual(1, len(self.h.outlet.packets))
|
self.assertEqual(1, len(self.h.outlet.packets))
|
||||||
|
|
||||||
# result = reader.read(1)
|
result = reader.read(1)
|
||||||
|
|
||||||
# self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
# self.assertTrue(self.h.channel.is_ready_to_send())
|
self.assertTrue(self.h.channel.is_ready_to_send())
|
||||||
|
|
||||||
# writer.close()
|
writer.close()
|
||||||
|
|
||||||
# self.assertEqual(2, len(self.h.outlet.packets))
|
self.assertEqual(2, len(self.h.outlet.packets))
|
||||||
|
|
||||||
# packet = self.h.outlet.packets[1]
|
packet = self.h.outlet.packets[1]
|
||||||
# self.h.channel._receive(packet.raw)
|
self.h.channel._receive(packet.raw)
|
||||||
# packet.delivered()
|
packet.delivered()
|
||||||
|
|
||||||
# result = reader.read(1)
|
result = reader.read(1)
|
||||||
|
|
||||||
# self.assertIsNotNone(result)
|
self.assertIsNotNone(result)
|
||||||
# self.assertTrue(len(result) == 0)
|
self.assertTrue(len(result) == 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
+37
-28
@@ -4,6 +4,7 @@ import subprocess
|
|||||||
import shlex
|
import shlex
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import random
|
||||||
from unittest import skipIf
|
from unittest import skipIf
|
||||||
import RNS
|
import RNS
|
||||||
import os
|
import os
|
||||||
@@ -23,6 +24,8 @@ fixed_keys = [
|
|||||||
("08bb35f92b06a0832991165a0d9b4fd91af7b7765ce4572aa6222070b11b767092b61b0fd18b3a59cae6deb9db6d4bfb1c7fcfe076cfd66eea7ddd5f877543b9", "d13712efc45ef87674fb5ac26c37c912"),
|
("08bb35f92b06a0832991165a0d9b4fd91af7b7765ce4572aa6222070b11b767092b61b0fd18b3a59cae6deb9db6d4bfb1c7fcfe076cfd66eea7ddd5f877543b9", "d13712efc45ef87674fb5ac26c37c912"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
BUFFER_TEST_TARGET = 32000
|
||||||
|
|
||||||
def targets_job(caller):
|
def targets_job(caller):
|
||||||
cmd = "python -c \"from tests.link import targets; targets()\""
|
cmd = "python -c \"from tests.link import targets; targets()\""
|
||||||
print("Opening subprocess for "+str(cmd)+"...", RNS.LOG_VERBOSE)
|
print("Opening subprocess for "+str(cmd)+"...", RNS.LOG_VERBOSE)
|
||||||
@@ -455,7 +458,7 @@ class TestLink(unittest.TestCase):
|
|||||||
|
|
||||||
# @skipIf(os.getenv('SKIP_NORMAL_TESTS') != None and os.getenv('RUN_SLOW_TESTS') == None, "Skipping")
|
# @skipIf(os.getenv('SKIP_NORMAL_TESTS') != None and os.getenv('RUN_SLOW_TESTS') == None, "Skipping")
|
||||||
def test_12_buffer_round_trip_big(self, local_bitrate = None):
|
def test_12_buffer_round_trip_big(self, local_bitrate = None):
|
||||||
global c_rns
|
global c_rns, buffer_read_target
|
||||||
init_rns(self)
|
init_rns(self)
|
||||||
print("")
|
print("")
|
||||||
print("Buffer round trip test")
|
print("Buffer round trip test")
|
||||||
@@ -490,9 +493,9 @@ class TestLink(unittest.TestCase):
|
|||||||
|
|
||||||
buffer = None
|
buffer = None
|
||||||
received = []
|
received = []
|
||||||
|
|
||||||
def handle_data(ready_bytes: int):
|
def handle_data(ready_bytes: int):
|
||||||
# TODO: Remove
|
global received_bytes
|
||||||
RNS.log("Handling data")
|
|
||||||
data = buffer.read(ready_bytes)
|
data = buffer.read(ready_bytes)
|
||||||
received.append(data)
|
received.append(data)
|
||||||
|
|
||||||
@@ -509,10 +512,11 @@ class TestLink(unittest.TestCase):
|
|||||||
if local_interface.bitrate < 1000:
|
if local_interface.bitrate < 1000:
|
||||||
target_bytes = 3000
|
target_bytes = 3000
|
||||||
else:
|
else:
|
||||||
target_bytes = 16000
|
target_bytes = BUFFER_TEST_TARGET
|
||||||
|
|
||||||
|
random.seed(154889)
|
||||||
message = os.urandom(target_bytes)
|
message = random.randbytes(target_bytes)
|
||||||
|
buffer_read_target = len(message)
|
||||||
|
|
||||||
# the return message will have an appendage string " back at you"
|
# the return message will have an appendage string " back at you"
|
||||||
# for every StreamDataMessage that arrives. To verify, we need
|
# for every StreamDataMessage that arrives. To verify, we need
|
||||||
@@ -527,35 +531,24 @@ class TestLink(unittest.TestCase):
|
|||||||
# since the segments will be received at max length for a
|
# since the segments will be received at max length for a
|
||||||
# StreamDataMessage, the appended text will end up in a
|
# StreamDataMessage, the appended text will end up in a
|
||||||
# separate packet.
|
# separate packet.
|
||||||
expected_chunk_count = ceil(len(message)/StreamDataMessage.MAX_DATA_LEN * 2)-1
|
print("Sending " + str(len(message)) + " bytes, receiving " + str(len(expected_rx_message)) + " bytes, ")
|
||||||
print("Sending " + str(len(message)) + " bytes, receiving " + str(len(expected_rx_message)) + " bytes, " +
|
|
||||||
"expecting " + str(expected_chunk_count) + " chunks of " + str(StreamDataMessage.MAX_DATA_LEN) + " bytes")
|
|
||||||
|
|
||||||
buffer.write(message)
|
buffer.write(message)
|
||||||
buffer.flush()
|
buffer.flush()
|
||||||
|
|
||||||
# delay a reasonable time for the send and receive
|
timeout = time.time() + 4
|
||||||
# a chunk each way plus a little more for a proof each way
|
while not time.time() > timeout:
|
||||||
# while time.time() < expected_ready_time and len(received) < expected_chunk_count:
|
time.sleep(1)
|
||||||
# time.sleep(0.1)
|
print(f"Received {len(received)} chunks so far")
|
||||||
# # sleep for at least one more chunk round trip in case there
|
time.sleep(1)
|
||||||
# # are more chunks than expected
|
|
||||||
# if time.time() < expected_ready_time:
|
|
||||||
# time.sleep(max(c_rns.MTU * 2 / local_interface.bitrate * 8, 1))
|
|
||||||
|
|
||||||
timeout = time.time() + 10
|
|
||||||
while len(received) < expected_chunk_count and not time.time() > timeout:
|
|
||||||
time.sleep(2)
|
|
||||||
print(f"Received {len(received)} out of {expected_chunk_count} chunks so far")
|
|
||||||
time.sleep(2)
|
|
||||||
print(f"Received {len(received)} out of {expected_chunk_count} chunks")
|
|
||||||
|
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
for rx in received:
|
for rx in received:
|
||||||
data.extend(rx)
|
data.extend(rx)
|
||||||
|
|
||||||
rx_message = data
|
rx_message = data
|
||||||
|
|
||||||
|
print(f"Received {len(received)} chunks, totalling {len(rx_message)} bytes")
|
||||||
|
|
||||||
self.assertEqual(len(expected_rx_message), len(rx_message))
|
self.assertEqual(len(expected_rx_message), len(rx_message))
|
||||||
for i in range(0, len(expected_rx_message)):
|
for i in range(0, len(expected_rx_message)):
|
||||||
self.assertEqual(expected_rx_message[i], rx_message[i])
|
self.assertEqual(expected_rx_message[i], rx_message[i])
|
||||||
@@ -598,7 +591,7 @@ class TestLink(unittest.TestCase):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=1)
|
unittest.main(verbosity=1)
|
||||||
|
|
||||||
|
buffer_read_len = 0
|
||||||
def targets(yp=False):
|
def targets(yp=False):
|
||||||
if yp:
|
if yp:
|
||||||
import yappi
|
import yappi
|
||||||
@@ -645,10 +638,26 @@ def targets(yp=False):
|
|||||||
|
|
||||||
buffer = None
|
buffer = None
|
||||||
|
|
||||||
|
response_data = []
|
||||||
def handle_buffer(ready_bytes: int):
|
def handle_buffer(ready_bytes: int):
|
||||||
|
global buffer_read_len, BUFFER_TEST_TARGET
|
||||||
data = buffer.read(ready_bytes)
|
data = buffer.read(ready_bytes)
|
||||||
buffer.write(data + " back at you".encode("utf-8"))
|
buffer_read_len += len(data)
|
||||||
buffer.flush()
|
response_data.append(data)
|
||||||
|
|
||||||
|
if data == "Hi there".encode("utf-8"):
|
||||||
|
RNS.log("Sending response")
|
||||||
|
for data in response_data:
|
||||||
|
buffer.write(data + " back at you".encode("utf-8"))
|
||||||
|
buffer.flush()
|
||||||
|
buffer_read_len = 0
|
||||||
|
|
||||||
|
if buffer_read_len == BUFFER_TEST_TARGET:
|
||||||
|
RNS.log("Sending response")
|
||||||
|
for data in response_data:
|
||||||
|
buffer.write(data + " back at you".encode("utf-8"))
|
||||||
|
buffer.flush()
|
||||||
|
buffer_read_len = 0
|
||||||
|
|
||||||
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_buffer)
|
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_buffer)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user