mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-07-23 16:28:12 -07:00
Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b17175dfef | |||
| 1103784997 | |||
| d2feb8b136 | |||
| f595648a9b | |||
| b06f5285c5 | |||
| 8330f70a27 | |||
| 15e10b9435 | |||
| b91c852330 | |||
| 75acdf5902 | |||
| dae40f2684 | |||
| 4edacf82f3 | |||
| 4b0a0668a5 | |||
| a52af17123 | |||
| 0b0a3313c5 | |||
| 34af2e7af7 | |||
| 12bf7977d2 | |||
| b69b939d6f | |||
| b5556f664b | |||
| f804ba0263 | |||
| 84a1ab0ca3 | |||
| 465695b9ae | |||
| a999a4a250 | |||
| cbb5d99280 | |||
| 64f5192c79 | |||
| d223ebc8c0 | |||
| c28f413fe6 | |||
| 92e5f65887 | |||
| b977f33df6 | |||
| 589fcb8201 | |||
| e5427d70ac | |||
| 2f5381b307 | |||
| 11baace08d | |||
| a4d5b5cb17 | |||
| 9cb181690e | |||
| ff6604290e | |||
| 2dbd3cbc0f | |||
| 2a11097cac | |||
| c0e3181ae3 | |||
| 5a0316ae7f | |||
| 177bb62610 | |||
| 7cd3cde398 | |||
| 29bdcea616 | |||
| d9460c43ad | |||
| fb02e980db | |||
| 4947463440 | |||
| 5565349255 | |||
| 1b7b131adc | |||
| ace0d997d4 | |||
| 798c252284 | |||
| 7da22c8580 | |||
| eefbb89cde | |||
| 18f50ff1ae | |||
| 05e97ac0db | |||
| c2c3a144d2 | |||
| ea369015ee | |||
| 9745842862 | |||
| 246289c52d | |||
| ff71cb2f98 | |||
| 5ca1ef1777 | |||
| 2b764b4af8 | |||
| a62843cd75 | |||
| 633435390d | |||
| 1e207ef972 | |||
| 35e9a0b38a | |||
| 3d7f3825fb | |||
| 04b67a545d | |||
| 61c2fbd0da | |||
| 1aba4ec43a | |||
| 841a3daa26 | |||
| d98f03f245 | |||
| 878e67f69d | |||
| e582a6d6d1 | |||
| a948afb816 | |||
| 86a294388f | |||
| 429a0b1bd3 | |||
| ee8bb42633 | |||
| c659388a2c | |||
| eaa8199988 | |||
| 4f890e7e8a | |||
| a37e039424 | |||
| 8e1e2a9c54 | |||
| e4f94c9d0b | |||
| b007530123 | |||
| 4066bba303 | |||
| 8951517d01 | |||
| ae1d962b9b | |||
| a2caa47334 | |||
| 9f43da9105 | |||
| 038c696db9 | |||
| 8fa6ec144c | |||
| a8ccff7c55 | |||
| a5783da407 | |||
| bec3cee425 | |||
| b15bd19de5 | |||
| 38390fd021 | |||
| 40e0eee64f | |||
| af4cbb1baf | |||
| d3f4192fe3 | |||
| 47ef62ac11 | |||
| d15ddc7a49 | |||
| d67c8eb1cd | |||
| f4de5d5199 | |||
| 34e42988ea | |||
| 81d5d41149 | |||
| 6b3f3a37f0 | |||
| 60a604f635 | |||
| 55a2daf379 | |||
| 2dbde13321 | |||
| 6620dcde6b | |||
| 60966d5bb1 | |||
| ea22a53bf2 | |||
| 7b9526b4ed | |||
| 676074187a | |||
| 5dd2c31caf | |||
| 2db400a1a0 | |||
| b68dbaf15e | |||
| 84febcdf95 | |||
| c972ef90c8 | |||
| 19a74e3130 | |||
| 5ba789f782 | |||
| 58b5501e17 | |||
| b584832b8f | |||
| fc0cf17c4d | |||
| 001dd369ec | |||
| 9ce2ea4a5c | |||
| eec8814c22 | |||
| 7a6ed68482 | |||
| cd9e23f2de | |||
| ffa84de0bc | |||
| 89d3cdba17 | |||
| 2ba5843f22 | |||
| c4d0f08767 | |||
| db1cdec2a2 | |||
| 1eea1a6a22 | |||
| 4a69ce5a98 | |||
| 8d653cba9b | |||
| a6126a6bc5 | |||
| 957c2b3bc1 | |||
| 494bde4e79 | |||
| 5e39136dff | |||
| 4b26a86a73 | |||
| 43a6e280c0 | |||
| 237a45b2ca | |||
| b161650ced | |||
| 24975eac31 | |||
| 5d1ff36565 | |||
| 628777900e | |||
| 12e87425dc | |||
| 873f049e20 | |||
| 2ea963ed03 | |||
| 1d1276d6dd | |||
| 83741724b0 | |||
| a4143cfe6d | |||
| 3d645ae2f4 | |||
| 5ba125c801 | |||
| badb392898 | |||
| c0e1ce8d86 | |||
| 0bc248c5e4 | |||
| 798dfb1727 | |||
| a451b987aa | |||
| f01074e5b8 | |||
| 0e12442a28 | |||
| a4e8489a34 | |||
| 276b6fbd22 | |||
| 52ab08c289 | |||
| 38236366cf | |||
| af3cc3c5dd | |||
| 35ed1f950c | |||
| c050ef945e | |||
| bed71fa3f8 | |||
| cf125daf5c | |||
| 9f425c2e8d | |||
| 0dc78241ac | |||
| 01e963e891 | |||
| b3731524ac | |||
| 67c7395ea7 | |||
| fddf36a920 | |||
| 4f561a8c0c | |||
| 778d6105c1 | |||
| 60c94dc9b6 | |||
| f71395e449 | |||
| 1abacca9bf | |||
| 40281d5403 |
@@ -0,0 +1,11 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: ✨ Feature Request or Idea
|
||||||
|
url: https://github.com/markqvist/Reticulum/discussions/new?category=ideas
|
||||||
|
about: Propose and discuss features and ideas
|
||||||
|
- name: 💬 Questions, Help & Discussion
|
||||||
|
about: Ask anything, or get help
|
||||||
|
url: https://github.com/markqvist/Reticulum/discussions/new/choose
|
||||||
|
- name: 📖 Read the Reticulum Manual
|
||||||
|
url: https://markqvist.github.io/Reticulum/manual/
|
||||||
|
about: The complete documentation for Reticulum
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: "\U0001F41B Bug Report"
|
||||||
|
about: Report a reproducible bug
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Read the Contribution Guidelines**
|
||||||
|
Before creating a bug report on this issue tracker, you **must** read the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md). Issues that do not follow the contribution guidelines **will be deleted without comment**.
|
||||||
|
|
||||||
|
- The issue tracker is used by developers of this project. **Do not use it to ask general questions, or for support requests**.
|
||||||
|
- Ideas and feature requests can be made on the [Discussions](https://github.com/markqvist/Reticulum/discussions). **Only** feature requests accepted by maintainers and developers are tracked and included on the issue tracker. **Do not post feature requests here**.
|
||||||
|
- After reading the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md), delete this section from your bug report.
|
||||||
|
|
||||||
|
**Describe the Bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Describe in detail how to reproduce the bug.
|
||||||
|
|
||||||
|
**Expected Behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Logs & Screenshots**
|
||||||
|
Please include any relevant log output. If applicable, also add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**System Information**
|
||||||
|
- OS and version
|
||||||
|
- Python version
|
||||||
|
- Program version
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
+204
-2
@@ -1,6 +1,208 @@
|
|||||||
|
### 2024-05-05: RNS β 0.7.4
|
||||||
|
|
||||||
|
This maintenance release fixes a number of bugs, improves path requests and responses, and adds several useful features and capabilities. Thanks to @cobraPA, @jsculthess, @thiaguetz and @nothingbutlucas who contributed to this release!
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added support for flashing and autoinstalling Heltec V3 boards to `rnodeconf`
|
||||||
|
- Added custom EEPROM bootstrapping capabilities to `rnodeconf`
|
||||||
|
- Added ability to load identities from file to Echo and Link examples
|
||||||
|
- Added ability to specify multicast address type in AutoInterface configuration
|
||||||
|
- Added link getter to resource advertisement class
|
||||||
|
- Improved path response logic and timing
|
||||||
|
- Improved path request timing
|
||||||
|
- Fixed a bug in Link Request proof delivery on unknown hop count paths
|
||||||
|
- Fixed broken link packet routing in topologies where transport packets leak to non-intended instances in the link chain
|
||||||
|
- Fixed typos in documentation
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
f5c35f1b8720778eb508b687d66334d01b4ab266b2d8c2bc186702220dcaae29 rns-0.7.4-py3-none-any.whl
|
||||||
|
9eaa7170f97dad49551136965d3fcc971b56b1c2eda48c24b9ffd58d71daa016 rnspure-0.7.4-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2024-03-09: RNS β 0.7.3
|
||||||
|
|
||||||
|
This release adds the ability to specify custom firmware URLs for flashing boards with `rnodeconf`. Thanks to @attermann who contributed to this release!
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added ability to specify custom firmware URLs for flashing boards with `rnodeconf`
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
bb24445ae9a3a63d348e4d7fe80b750608f257851b97b38fadab929b7a774bc9 rns-0.7.3-py3-none-any.whl
|
||||||
|
1b148d013103c35ba9a8e105082ef50686c130676d0a560ed709cb546129287e rnspure-0.7.3-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2024-03-02: RNS β 0.7.2
|
||||||
|
|
||||||
|
This maintenance release improves memory consumption, fixes a few bugs, and adds ability to flash new boards with `rnodeconf`.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added ability to flash new boards with `rnodeconf`, including T3 boards with TCXOs
|
||||||
|
- Improved memory consumption on Transport Instances with many interfaces
|
||||||
|
- Fixed a bug that could cause the on-disk known destinations store to become corrupted
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
3ce3ba80d5ae8d19c6b55bd51f44bd4beccbcea31554cb1f0d65428e4587b3d6 rns-0.7.2-py3-none-any.whl
|
||||||
|
83f914aaba2a8929a8cee95830a847e190197232a0cca4e7b906b15c6bbf8296 rnspure-0.7.2-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2024-02-14: RNS β 0.7.1
|
||||||
|
|
||||||
|
This release adds support for RNodes based on SX1262, SX1268 and SX1280 modems, and fixes a number of bugs. Thanks to @jacobeva, who contributed to this release!
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added support for SX1262, SX1268 and SX1280 based RNodes
|
||||||
|
- Updated `rnodeconf` to allow flashing T-Beam devices with SX126x chips
|
||||||
|
- Fixed an invalid RSSI offset reference
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
8ecfbb42b6a699fd4ac5374ab5640e4bb164e80bb9ab4401ea82da132e497877 rns-0.7.1-py3-none-any.whl
|
||||||
|
e0ab487305ba1aee2d16044640e7eb72d332bbf51aeb0b8bf984d037a64cb493 rnspure-0.7.1-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2024-01-17: RNS β 0.7.0
|
||||||
|
|
||||||
|
This maintenance release fixes a number of bugs. Thanks to @jooray and @jacobeva, who contributed to this release!
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Fixed large resource transfers failing under some conditions
|
||||||
|
- Fixed a potential division by zero
|
||||||
|
- Fixed a missing check on malformed advertisement packets
|
||||||
|
- Fixed a formatting issue in `rnprobe`
|
||||||
|
- Improved resource timeout calculations
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
0dc2abe5373b9afadfba7ec05bf7ddeff659c004aa339a94001ebed5b46f5b47 rns-0.7.0-py3-none-any.whl
|
||||||
|
97f6e65a20b53bbdccd54b4d2bdaa36dc1712e144a55f40800c63fe7113819a5 rnspure-0.7.0-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-12-07: RNS β 0.6.9
|
||||||
|
|
||||||
|
This release adds a few convenience functions to the `rnid` utility, and improves roaming support on Android.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added identity import and export in hex, base32 and base64 formats to the `rnid` utility.
|
||||||
|
- Added better carrier change detection for AutoInterface on Android.
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
258daf22cb6e72c6cd04fe94447daedf51dfd968eb2f3370eab9c71ad0898dd0 rns-0.6.9-py3-none-any.whl
|
||||||
|
3644b64af5b4efd3969172bf0cf95ae1afba6c8ea99ce47d8e49e31a832bbaf8 rnspure-0.6.9-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-11-14: RNS β 0.6.8
|
||||||
|
|
||||||
|
This maintenance release fixes a single bug.
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed packet receipts not being initialised in time for arriving proofs on fast interfaces
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
3ffb01f3f45e35105ea30e60e5e493ba50528df38b4ea62672c9e1c093073b1c rns-0.6.8-py3-none-any.whl
|
||||||
|
de372814082ef7db59f4b2745b1f22b2ef9d97815190ec16c0596ba20406e0fb rnspure-0.6.8-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-11-06: RNS β 0.6.7
|
||||||
|
|
||||||
|
This maintenance release improves tranport performance and fixes a logging bug.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Improved local and remote transport performance by approximately 6x on faster links
|
||||||
|
- Significantly decreased latency over faster links
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed logging an error message when local clients connect while shared instance is still starting up
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
c37dd1f59e037841f69ec518deecdae6719f978947de2473f04e7d95247805ac rns-0.6.7-py3-none-any.whl
|
||||||
|
1e2dcb44ec7271a4d26180db138fc54dce6d0d3cf3f816432d4d6a4b1cf83868 rnspure-0.6.7-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-11-04: RNS β 0.6.6
|
||||||
|
|
||||||
|
This maintenance release improves transfers over unreliable links and fixes a bug in requests.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Improved reliability of resource transfers over very slow and unreliable links
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed a bug that could cause requests to timeout prematurely
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
b1127745750a43cd7389212d31aa09ccc735ab2d69e3b80bd28874f10082c322 rns-0.6.6-py3-none-any.whl
|
||||||
|
bf5ba5da4f37b93c14817367952cda63787ec88bbe601e41c13fcbb3fc22b6b6 rnspure-0.6.6-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-11-02: RNS β 0.6.5
|
||||||
|
|
||||||
|
This release fixes a bug in path rediscovery for shared instance clients.
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed a bug in path rediscovery for shared instance clients
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
5d54a5cfebe907c759351357a8f7d771670c895ff57f1325bf7fec42bcb46ba3 rns-0.6.5-py3-none-any.whl
|
||||||
|
accd2855e18ff06455b9454957388089e293073ec7093c64dee0dc7aa46ecb46 rnspure-0.6.5-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-11-02: RNS β 0.6.4
|
||||||
|
|
||||||
|
This release fixes a number of bugs that had crept in while adding the new ultra low bandwidth link timing and faster path rediscovery mechanisms.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Adjusted link timings for better support of very slow mediums
|
||||||
|
- Adjusted bluetooth read timeouts to account for occasional high latency in congested 2.4GHz environments
|
||||||
|
- Added a probe count option to the `rnprobe` utility.
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed a missing timeout calculation
|
||||||
|
- Fixed a redundant path request on path rediscovery
|
||||||
|
- Fixed missing path state resetting on stale path rediscovery
|
||||||
|
- Fixed a bug that could cause an attribute to be uninitialised
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
566c725f68aa154eaca0880c894a39503027bf91714f17691e51d047800444c0 rns-0.6.4-py3-none-any.whl
|
||||||
|
a3a447fd40bf02fdb982523de0e4e9933e8e4cd4d4bd478254ea7dcac29e3fc1 rnspure-0.6.4-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2023-10-31: RNS β 0.6.3
|
||||||
|
|
||||||
|
This release brings a series of under-the-hood reliability improvements and bugfixes. But most notably, Reticulum can now establish links over even ultra low bandwidth mediums, all the way down to 5 bits per second.
|
||||||
|
|
||||||
|
Thanks to @jschulthess, who contributed to this release!
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Implemented link establishment on ultra low bandwidth links
|
||||||
|
- Added link quality calculations to RNode interfaces
|
||||||
|
- Added physical layer link stats to Link and Packet classes
|
||||||
|
- Added userspace service documentation to the manual
|
||||||
|
- Improved path rediscovery in quickly changing topographies
|
||||||
|
- Improved shared interface reconnection on service restart
|
||||||
|
- Improved exception handling on interface detachment
|
||||||
|
- Updated formatted print functions
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
- Fixed a missing USB command definition in the RNode interface driver
|
||||||
|
- Fixed a bug in link error handling that could cause an interface to detach
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
1f54d4c6ff7ab7721089cbee6630783765f65efd51312879c0d3e5bee3ceab2f rns-0.6.3-py3-none-any.whl
|
||||||
|
5a90840f0fc9f1a62a3c37b514fb6222fd701a30024275dae8bcc27e29d40f25 rnspure-0.6.3-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
### 2023-10-07: RNS β 0.6.2
|
### 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.
|
This maintenance release adds the ability to specify the shared instance RPC key in the Reticulum config file, making it possible to use all Reticulum functionality in the terminal on Android.
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
- Added configuration option to specify shared instance RPC key
|
- Added configuration option to specify shared instance RPC key
|
||||||
@@ -943,4 +1145,4 @@ This was the first publicly available pre-release alpha of Reticulum.
|
|||||||
|
|
||||||
### 2016-05-29: Inintial Repository Commit
|
### 2016-05-29: Inintial Repository Commit
|
||||||
|
|
||||||
The first commit to the Reticulum reference implementation was 9a9630cfd29e11ace3f12716ddb4dff0e5419b4b, which occurred on Sunday, the 22nd of May 2016.
|
The first commit to the Reticulum reference implementation was 9a9630cfd29e11ace3f12716ddb4dff0e5419b4b, which occurred on Sunday, the 29th of May 2016.
|
||||||
|
|||||||
+27
-7
@@ -2,22 +2,42 @@
|
|||||||
|
|
||||||
Welcome, and thank you for your interest in contributing to Reticulum!
|
Welcome, and thank you for your interest in contributing to Reticulum!
|
||||||
|
|
||||||
Apart from writing code, there are many ways in which you can contribute. Before getting started, please read these guidelines.
|
Apart from writing code, there are many ways in which you can contribute. Before interacting with this community, read these short and simple guidelines.
|
||||||
|
|
||||||
|
## Expected Conduct
|
||||||
|
|
||||||
|
First and foremost, there is one simple requirement for taking part in this community: While we primarily interact virtually, your actions matter and have real consequences. Therefore: **Act like a responsible, civilized person** - also in the face of disputes and heated disagreements. Speak your mind here, discussions are welcome. Just do so in the spirit of being face-to-face with everyone else. Thank you.
|
||||||
|
|
||||||
## Asking Questions
|
## Asking Questions
|
||||||
|
|
||||||
If you want to ask a question, do not open an issue.
|
If you want to ask a question, **do not open an issue**. The issue tracker is used by people *working on Reticulum* to track bugs, issues and improvements.
|
||||||
|
|
||||||
Instead, ask away on the [discussions](https://github.com/markqvist/Reticulum/discussions) or on the [Reticulum Matrix channel](https://unsigned.io/contact.html#reticulum:matrix.org) at `#reticulum:matrix.org`
|
Instead, ask away on the [discussions](https://github.com/markqvist/Reticulum/discussions) or on the [Reticulum Matrix channel](https://matrix.to/#/#reticulum:matrix.org) at `#reticulum:matrix.org`
|
||||||
|
|
||||||
## Providing Feedback
|
## Providing Feedback & Ideas
|
||||||
|
|
||||||
Likewise, feedback, ideas and feature requests are a very welcome way to contribute, and should also be posted on the [discussions](https://github.com/markqvist/Reticulum/discussions), or on the [Reticulum Matrix channel](https://unsigned.io/contact.html#reticulum:matrix.org) at `#reticulum:matrix.org`
|
Likewise, feedback, ideas and feature requests are a very welcome way to contribute, and should also be posted on the [discussions](https://github.com/markqvist/Reticulum/discussions), or on the [Reticulum Matrix channel](https://matrix.to/#/#reticulum:matrix.org) at `#reticulum:matrix.org`.
|
||||||
|
|
||||||
|
Please do not post feature requests or general ideas on the issue tracker, or in direct messages to the primary developers. You are much more likely to get a response and start a constructive discussion by posting your ideas in the public channels created for these purposes.
|
||||||
|
|
||||||
## Reporting Issues
|
## Reporting Issues
|
||||||
|
|
||||||
If you have found a bug or issue in Reticulum, please report it on the [issue tracker](https://github.com/markqvist/Reticulum/issues).
|
If you have found a bug or issue in this project, please report it using the [issue tracker](https://github.com/markqvist/Reticulum/issues). If at all possible, be sure to include details on how to reproduce the bug.
|
||||||
|
|
||||||
|
Anything submitted to the issue tracker that does not follow these guidelines will be closed and removed without comments or explanation.
|
||||||
|
|
||||||
## Writing Code
|
## Writing Code
|
||||||
|
|
||||||
If you are interested in contributing code, fixing open issues or adding features, please coordinate the effort with the maintainer or one of the main developers first, to ensure your efforts are in alignment with the [Roadmap](./Roadmap.md) and current development focus.
|
If you are interested in contributing code, fixing open issues or adding features, please coordinate the effort with the maintainer or one of the main developers **before** submitting a pull request. Before deciding to contribute, it is also a good idea to ensure your efforts are in alignment with the [Roadmap](./Roadmap.md) and current development focus.
|
||||||
|
|
||||||
|
Pull requests have a high chance of being accepted if they are:
|
||||||
|
|
||||||
|
- In alignment with the [Roadmap](./Roadmap.md) or solve an open issue or feature request
|
||||||
|
- Sufficiently tested to work with all API functions, and pass the standard test suite
|
||||||
|
- Functionally and conceptually complete and well-designed
|
||||||
|
- Not simply formatting or code style changes
|
||||||
|
- Well-documented
|
||||||
|
|
||||||
|
Even new ideas and proposals that have not been approved by a maintainer, or fall outside the established roadmap, are *occasionally* accepted - if they possess the remaining of the above qualities. If not, they will be closed and removed without comments or explanation.
|
||||||
|
|
||||||
|
By contributing code to this project, you agree that copyright for the code is transferred to the Reticulum maintainers and that the code is irrevocably placed under the [MIT license](./LICENSE).
|
||||||
|
|||||||
+13
-1
@@ -5,6 +5,7 @@
|
|||||||
# of the packet. #
|
# of the packet. #
|
||||||
##########################################################
|
##########################################################
|
||||||
|
|
||||||
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import RNS
|
import RNS
|
||||||
|
|
||||||
@@ -27,8 +28,19 @@ def server(configpath):
|
|||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our echo server
|
# Load identity from file if it exist or randomley create
|
||||||
|
if configpath:
|
||||||
|
ifilepath = "%s/storage/identitiesy/%s" % (configpath,APP_NAME)
|
||||||
|
else:
|
||||||
|
ifilepath = "%s/storage/identities/%s" % (RNS.Reticulum.configdir,APP_NAME)
|
||||||
|
if os.path.exists(ifilepath):
|
||||||
|
# Load identity from file
|
||||||
|
server_identity = RNS.Identity.from_file(ifilepath)
|
||||||
|
RNS.log("loaded identity from file: "+ifilepath, RNS.LOG_VERBOSE)
|
||||||
|
else:
|
||||||
|
# Randomly create a new identity for our echo example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
RNS.log("created new identity", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
# We create a destination that clients can query. We want
|
# We create a destination that clients can query. We want
|
||||||
# to be able to verify echo replies to our clients, so we
|
# to be able to verify echo replies to our clients, so we
|
||||||
|
|||||||
@@ -449,8 +449,7 @@ def link_established(link):
|
|||||||
# And set up a small job to check for
|
# And set up a small job to check for
|
||||||
# a potential timeout in receiving the
|
# a potential timeout in receiving the
|
||||||
# file list
|
# file list
|
||||||
thread = threading.Thread(target=filelist_timeout_job)
|
thread = threading.Thread(target=filelist_timeout_job, daemon=True)
|
||||||
thread.setDaemon(True)
|
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
# This job just sleeps for the specified
|
# This job just sleeps for the specified
|
||||||
|
|||||||
@@ -28,8 +28,20 @@ def server(configpath):
|
|||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
|
# Load identity from file if it exist or randomley create
|
||||||
|
if configpath:
|
||||||
|
ifilepath = "%s/storage/identitiesy/%s" % (configpath,APP_NAME)
|
||||||
|
else:
|
||||||
|
ifilepath = "%s/storage/identities/%s" % (RNS.Reticulum.configdir,APP_NAME)
|
||||||
|
RNS.log("ifilepath: %s" % ifilepath)
|
||||||
|
if os.path.exists(ifilepath):
|
||||||
|
# Load identity from file
|
||||||
|
server_identity = RNS.Identity.from_file(ifilepath)
|
||||||
|
RNS.log("loaded identity from file: "+ifilepath, RNS.LOG_VERBOSE)
|
||||||
|
else:
|
||||||
# Randomly create a new identity for our link example
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
RNS.log("created new identity", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
# We create a destination that clients can connect to. We
|
# We create a destination that clients can connect to. We
|
||||||
# want clients to create links to this destination, so we
|
# want clients to create links to this destination, so we
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ko_fi: markqvist
|
||||||
|
custom: "https://unsigned.io/donate"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License, unless otherwise noted
|
MIT License, unless otherwise noted
|
||||||
|
|
||||||
Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
Copyright (c) 2016-2024 Mark Qvist / unsigned.io
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
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
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ For more info, see [reticulum.network](https://reticulum.network/)
|
|||||||
- Initiator anonymity, communicate without revealing your identity
|
- Initiator anonymity, communicate without revealing your identity
|
||||||
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
||||||
- Forward Secrecy with ephemeral Elliptic Curve Diffie-Hellman keys on Curve25519
|
- Forward Secrecy with ephemeral Elliptic Curve Diffie-Hellman keys on Curve25519
|
||||||
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for on-the-wire / over-the-air encryption
|
- Reticulum uses the following format for encrypted tokens:
|
||||||
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
|
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
|
||||||
- AES-128 in CBC mode with PKCS7 padding
|
- AES-128 in CBC mode with PKCS7 padding
|
||||||
- HMAC using SHA256 for authentication
|
- HMAC using SHA256 for authentication
|
||||||
@@ -80,7 +80,7 @@ following resources.
|
|||||||
|
|
||||||
## Where can Reticulum be used?
|
## Where can Reticulum be used?
|
||||||
Over practically any medium that can support at least a half-duplex channel
|
Over practically any medium that can support at least a half-duplex channel
|
||||||
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
|
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
|
||||||
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
||||||
WiFi and Ethernet devices, free-space optical links, and similar systems are
|
WiFi and Ethernet devices, free-space optical links, and similar systems are
|
||||||
all examples of the types of physical devices Reticulum can use.
|
all examples of the types of physical devices Reticulum can use.
|
||||||
@@ -191,8 +191,8 @@ functionality and performance on low-bandwidth mediums. The goal is to
|
|||||||
provide a dynamic performance envelope from 250 bits per second, to 1 gigabit
|
provide a dynamic performance envelope from 250 bits per second, to 1 gigabit
|
||||||
per second on normal hardware.
|
per second on normal hardware.
|
||||||
|
|
||||||
Currently, the usable performance envelope is approximately 500 bits per second
|
Currently, the usable performance envelope is approximately 150 bits per second
|
||||||
to 20 megabits per second, with physical mediums faster than that not being
|
to 40 megabits per second, with physical mediums faster than that not being
|
||||||
saturated. Performance beyond the current level is intended for future
|
saturated. Performance beyond the current level is intended for future
|
||||||
upgrades, but not highly prioritised at this point in time.
|
upgrades, but not highly prioritised at this point in time.
|
||||||
|
|
||||||
@@ -278,11 +278,11 @@ You can help support the continued development of open, free and private communi
|
|||||||
```
|
```
|
||||||
- Ethereum
|
- Ethereum
|
||||||
```
|
```
|
||||||
0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a
|
0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73
|
||||||
```
|
```
|
||||||
- Bitcoin
|
- Bitcoin
|
||||||
```
|
```
|
||||||
3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq
|
35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH
|
||||||
```
|
```
|
||||||
- Ko-Fi: https://ko-fi.com/markqvist
|
- Ko-Fi: https://ko-fi.com/markqvist
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ class Fernet():
|
|||||||
|
|
||||||
def __init__(self, key = None):
|
def __init__(self, key = None):
|
||||||
if key == None:
|
if key == None:
|
||||||
raise ValueError("Fernet key cannot be None")
|
raise ValueError("Token key cannot be None")
|
||||||
|
|
||||||
if len(key) != 32:
|
if len(key) != 32:
|
||||||
raise ValueError("Fernet key must be 32 bytes, not "+str(len(key)))
|
raise ValueError("Token key must be 32 bytes, not "+str(len(key)))
|
||||||
|
|
||||||
self._signing_key = key[:16]
|
self._signing_key = key[:16]
|
||||||
self._encryption_key = key[16:]
|
self._encryption_key = key[16:]
|
||||||
@@ -72,7 +72,7 @@ class Fernet():
|
|||||||
current_time = int(time.time())
|
current_time = int(time.time())
|
||||||
|
|
||||||
if not isinstance(data, bytes):
|
if not isinstance(data, bytes):
|
||||||
raise TypeError("Fernet token plaintext input must be bytes")
|
raise TypeError("Token plaintext input must be bytes")
|
||||||
|
|
||||||
ciphertext = AES_128_CBC.encrypt(
|
ciphertext = AES_128_CBC.encrypt(
|
||||||
plaintext = PKCS7.pad(data),
|
plaintext = PKCS7.pad(data),
|
||||||
@@ -87,10 +87,10 @@ class Fernet():
|
|||||||
|
|
||||||
def decrypt(self, token = None):
|
def decrypt(self, token = None):
|
||||||
if not isinstance(token, bytes):
|
if not isinstance(token, bytes):
|
||||||
raise TypeError("Fernet token must be bytes")
|
raise TypeError("Token must be bytes")
|
||||||
|
|
||||||
if not self.verify_hmac(token):
|
if not self.verify_hmac(token):
|
||||||
raise ValueError("Fernet token HMAC was invalid")
|
raise ValueError("Token HMAC was invalid")
|
||||||
|
|
||||||
iv = token[:16]
|
iv = token[:16]
|
||||||
ciphertext = token[16:-32]
|
ciphertext = token[16:-32]
|
||||||
@@ -107,4 +107,4 @@ class Fernet():
|
|||||||
return plaintext
|
return plaintext
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError("Could not decrypt Fernet token")
|
raise ValueError("Could not decrypt token")
|
||||||
+6
-1
@@ -145,9 +145,12 @@ class Identity:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
for destination_hash in storage_known_destinations:
|
for destination_hash in storage_known_destinations:
|
||||||
if not destination_hash in Identity.known_destinations:
|
if not destination_hash in Identity.known_destinations:
|
||||||
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
|
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Skipped recombining known destinations from disk, since an error occurred: "+str(e), RNS.LOG_WARNING)
|
||||||
|
|
||||||
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_DEBUG)
|
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_DEBUG)
|
||||||
file = open(RNS.Reticulum.storagepath+"/known_destinations","wb")
|
file = open(RNS.Reticulum.storagepath+"/known_destinations","wb")
|
||||||
@@ -164,6 +167,7 @@ class Identity:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while saving known destinations to disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while saving known destinations to disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
RNS.trace_exception(e)
|
||||||
|
|
||||||
Identity.saving_known_destinations = False
|
Identity.saving_known_destinations = False
|
||||||
|
|
||||||
@@ -181,7 +185,8 @@ class Identity:
|
|||||||
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
|
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
|
||||||
|
|
||||||
RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destination from storage", RNS.LOG_VERBOSE)
|
RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destination from storage", RNS.LOG_VERBOSE)
|
||||||
except:
|
|
||||||
|
except Exception as e:
|
||||||
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
|
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
|
||||||
else:
|
else:
|
||||||
RNS.log("Destinations file does not exist, no known destinations loaded", RNS.LOG_VERBOSE)
|
RNS.log("Destinations file does not exist, no known destinations loaded", RNS.LOG_VERBOSE)
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class KISS():
|
|||||||
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_BT_CTRL = 0x46
|
||||||
CMD_PLATFORM = 0x48
|
CMD_PLATFORM = 0x48
|
||||||
CMD_MCU = 0x49
|
CMD_MCU = 0x49
|
||||||
CMD_FW_VERSION = 0x50
|
CMD_FW_VERSION = 0x50
|
||||||
@@ -114,7 +115,7 @@ class AndroidBluetoothManager():
|
|||||||
if self.bt_enabled():
|
if self.bt_enabled():
|
||||||
return self.bt_adapter.getDefaultAdapter().getBondedDevices()
|
return self.bt_adapter.getDefaultAdapter().getBondedDevices()
|
||||||
else:
|
else:
|
||||||
RNS.log("Could not query paired devices, Bluetooth is disabled", RNS.LOG_DEBUG)
|
RNS.log("Could not query paired devices, Bluetooth is disabled", RNS.LOG_EXTREME)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_potential_devices(self):
|
def get_potential_devices(self):
|
||||||
@@ -166,8 +167,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_DEBUG)
|
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_EXTREME)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_EXTREME)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self.connected:
|
if self.connected:
|
||||||
@@ -232,6 +233,10 @@ class RNodeInterface(Interface):
|
|||||||
RECONNECT_WAIT = 5
|
RECONNECT_WAIT = 5
|
||||||
PORT_IO_TIMEOUT = 3
|
PORT_IO_TIMEOUT = 3
|
||||||
|
|
||||||
|
Q_SNR_MIN_BASE = -9
|
||||||
|
Q_SNR_MAX = 6
|
||||||
|
Q_SNR_STEP = 2
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bluetooth_control(device_serial = None, port = None, enable_bluetooth = False, disable_bluetooth = False, pairing_mode = False):
|
def bluetooth_control(device_serial = None, port = None, enable_bluetooth = False, disable_bluetooth = False, pairing_mode = False):
|
||||||
if (port != None or device_serial != None) and (enable_bluetooth or disable_bluetooth or pairing_mode):
|
if (port != None or device_serial != None) and (enable_bluetooth or disable_bluetooth or pairing_mode):
|
||||||
@@ -597,8 +602,11 @@ class RNodeInterface(Interface):
|
|||||||
raise IOError("Invalid device firmware")
|
raise IOError("Invalid device firmware")
|
||||||
|
|
||||||
if self.serial != None and self.port != None:
|
if self.serial != None and self.port != None:
|
||||||
|
self.timeout = 200
|
||||||
RNS.log("Serial port "+self.port+" is now open")
|
RNS.log("Serial port "+self.port+" is now open")
|
||||||
|
|
||||||
if self.bt_manager != None and self.bt_manager.connected:
|
if self.bt_manager != None and self.bt_manager.connected:
|
||||||
|
self.timeout = 1500
|
||||||
RNS.log("Bluetooth connection to RNode now open")
|
RNS.log("Bluetooth connection to RNode now open")
|
||||||
|
|
||||||
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
|
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
|
||||||
@@ -860,9 +868,6 @@ class RNodeInterface(Interface):
|
|||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
threading.Thread(target=af, daemon=True).start()
|
threading.Thread(target=af, daemon=True).start()
|
||||||
|
|
||||||
self.r_stat_rssi = None
|
|
||||||
self.r_stat_snr = None
|
|
||||||
|
|
||||||
|
|
||||||
def processOutgoing(self,data):
|
def processOutgoing(self,data):
|
||||||
datalen = len(data)
|
datalen = len(data)
|
||||||
@@ -1044,6 +1049,19 @@ 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
|
||||||
|
try:
|
||||||
|
sfs = self.r_sf-7
|
||||||
|
snr = self.r_stat_snr
|
||||||
|
q_snr_min = RNodeInterface.Q_SNR_MIN_BASE-sfs*RNodeInterface.Q_SNR_STEP
|
||||||
|
q_snr_max = RNodeInterface.Q_SNR_MAX
|
||||||
|
q_snr_span = q_snr_max-q_snr_min
|
||||||
|
quality = round(((snr-q_snr_min)/(q_snr_span))*100,1)
|
||||||
|
if quality > 100.0: quality = 100.0
|
||||||
|
if quality < 0.0: quality = 0.0
|
||||||
|
self.r_stat_q = quality
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
elif (command == KISS.CMD_ST_ALOCK):
|
elif (command == KISS.CMD_ST_ALOCK):
|
||||||
if (byte == KISS.FESC):
|
if (byte == KISS.FESC):
|
||||||
escape = True
|
escape = True
|
||||||
@@ -1155,7 +1173,7 @@ class RNodeInterface(Interface):
|
|||||||
if got == 0:
|
if got == 0:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
RNS.log(str(self)+" serial read timeout", RNS.LOG_DEBUG)
|
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
|
||||||
data_buffer = b""
|
data_buffer = b""
|
||||||
in_frame = False
|
in_frame = False
|
||||||
command = KISS.CMD_UNKNOWN
|
command = KISS.CMD_UNKNOWN
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class AutoInterface(Interface):
|
|||||||
SCOPE_ORGANISATION = "8"
|
SCOPE_ORGANISATION = "8"
|
||||||
SCOPE_GLOBAL = "e"
|
SCOPE_GLOBAL = "e"
|
||||||
|
|
||||||
|
MULTICAST_PERMANENT_ADDRESS_TYPE = "0"
|
||||||
|
MULTICAST_TEMPORARY_ADDRESS_TYPE = "1"
|
||||||
|
|
||||||
PEERING_TIMEOUT = 7.5
|
PEERING_TIMEOUT = 7.5
|
||||||
|
|
||||||
ALL_IGNORE_IFS = ["lo0"]
|
ALL_IGNORE_IFS = ["lo0"]
|
||||||
@@ -74,7 +77,7 @@ class AutoInterface(Interface):
|
|||||||
ifas = self.netinfo.ifaddresses(ifname)
|
ifas = self.netinfo.ifaddresses(ifname)
|
||||||
return ifas
|
return ifas
|
||||||
|
|
||||||
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, multicast_address_type=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__()
|
super().__init__()
|
||||||
self.netinfo = niwrapper
|
self.netinfo = niwrapper
|
||||||
@@ -128,6 +131,15 @@ class AutoInterface(Interface):
|
|||||||
else:
|
else:
|
||||||
self.discovery_port = discovery_port
|
self.discovery_port = discovery_port
|
||||||
|
|
||||||
|
if multicast_address_type == None:
|
||||||
|
self.multicast_address_type = AutoInterface.MULTICAST_TEMPORARY_ADDRESS_TYPE
|
||||||
|
elif str(multicast_address_type).lower() == "temporary":
|
||||||
|
self.multicast_address_type = AutoInterface.MULTICAST_TEMPORARY_ADDRESS_TYPE
|
||||||
|
elif str(multicast_address_type).lower() == "permanent":
|
||||||
|
self.multicast_address_type = AutoInterface.MULTICAST_PERMANENT_ADDRESS_TYPE
|
||||||
|
else:
|
||||||
|
self.multicast_address_type = AutoInterface.MULTICAST_TEMPORARY_ADDRESS_TYPE
|
||||||
|
|
||||||
if data_port == None:
|
if data_port == None:
|
||||||
self.data_port = AutoInterface.DEFAULT_DATA_PORT
|
self.data_port = AutoInterface.DEFAULT_DATA_PORT
|
||||||
else:
|
else:
|
||||||
@@ -156,7 +168,7 @@ class AutoInterface(Interface):
|
|||||||
gt += ":"+"{:02x}".format(g[9]+(g[8]<<8))
|
gt += ":"+"{:02x}".format(g[9]+(g[8]<<8))
|
||||||
gt += ":"+"{:02x}".format(g[11]+(g[10]<<8))
|
gt += ":"+"{:02x}".format(g[11]+(g[10]<<8))
|
||||||
gt += ":"+"{:02x}".format(g[13]+(g[12]<<8))
|
gt += ":"+"{:02x}".format(g[13]+(g[12]<<8))
|
||||||
self.mcast_discovery_address = "ff1"+self.discovery_scope+":"+gt
|
self.mcast_discovery_address = "ff"+self.multicast_address_type+self.discovery_scope+":"+gt
|
||||||
|
|
||||||
suitable_interfaces = 0
|
suitable_interfaces = 0
|
||||||
for ifname in self.list_interfaces():
|
for ifname in self.list_interfaces():
|
||||||
@@ -334,6 +346,8 @@ class AutoInterface(Interface):
|
|||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
self.carrier_changed = True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class Interface:
|
|||||||
self.txb = 0
|
self.txb = 0
|
||||||
self.created = time.time()
|
self.created = time.time()
|
||||||
self.online = False
|
self.online = False
|
||||||
|
self.bitrate = 1e6
|
||||||
|
|
||||||
self.ingress_control = True
|
self.ingress_control = True
|
||||||
self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES
|
self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import RNS
|
import RNS
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
class HDLC():
|
class HDLC():
|
||||||
FLAG = 0x7E
|
FLAG = 0x7E
|
||||||
@@ -50,7 +51,7 @@ class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
|||||||
self.server_address = self.socket.getsockname()
|
self.server_address = self.socket.getsockname()
|
||||||
|
|
||||||
class LocalClientInterface(Interface):
|
class LocalClientInterface(Interface):
|
||||||
RECONNECT_WAIT = 3
|
RECONNECT_WAIT = 8
|
||||||
|
|
||||||
def __init__(self, owner, name, target_port = None, connected_socket=None):
|
def __init__(self, owner, name, target_port = None, connected_socket=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -77,6 +78,7 @@ class LocalClientInterface(Interface):
|
|||||||
self.target_ip = None
|
self.target_ip = None
|
||||||
self.target_port = None
|
self.target_port = None
|
||||||
self.socket = connected_socket
|
self.socket = connected_socket
|
||||||
|
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
|
||||||
self.is_connected_to_shared_instance = False
|
self.is_connected_to_shared_instance = False
|
||||||
|
|
||||||
@@ -107,6 +109,7 @@ class LocalClientInterface(Interface):
|
|||||||
|
|
||||||
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.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
self.socket.connect((self.target_ip, self.target_port))
|
self.socket.connect((self.target_ip, self.target_port))
|
||||||
|
|
||||||
self.online = True
|
self.online = True
|
||||||
@@ -139,7 +142,10 @@ class LocalClientInterface(Interface):
|
|||||||
thread = threading.Thread(target=self.read_loop)
|
thread = threading.Thread(target=self.read_loop)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
def job():
|
||||||
|
time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
|
||||||
RNS.Transport.shared_connection_reappeared()
|
RNS.Transport.shared_connection_reappeared()
|
||||||
|
threading.Thread(target=job, daemon=True).start()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
|
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
|
||||||
@@ -147,9 +153,6 @@ class LocalClientInterface(Interface):
|
|||||||
|
|
||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
if self._force_bitrate:
|
|
||||||
time.sleep(len(data) / self.bitrate * 8)
|
|
||||||
|
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||||
self.parent_interface.rxb += len(data)
|
self.parent_interface.rxb += len(data)
|
||||||
@@ -167,8 +170,16 @@ class LocalClientInterface(Interface):
|
|||||||
if self.online:
|
if self.online:
|
||||||
try:
|
try:
|
||||||
self.writing = True
|
self.writing = True
|
||||||
|
|
||||||
if self._force_bitrate:
|
if self._force_bitrate:
|
||||||
time.sleep(len(data) / self.bitrate * 8)
|
if not hasattr(self, "send_lock"):
|
||||||
|
self.send_lock = Lock()
|
||||||
|
|
||||||
|
with self.send_lock:
|
||||||
|
s = len(data) / self.bitrate * 8
|
||||||
|
RNS.log(f"Simulating latency of {RNS.prettytime(s)} for {len(data)} bytes")
|
||||||
|
time.sleep(s)
|
||||||
|
|
||||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||||
self.socket.sendall(data)
|
self.socket.sendall(data)
|
||||||
self.writing = False
|
self.writing = False
|
||||||
@@ -261,6 +272,7 @@ class LocalClientInterface(Interface):
|
|||||||
RNS.Transport.local_client_interfaces.remove(self)
|
RNS.Transport.local_client_interfaces.remove(self)
|
||||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||||
self.parent_interface.clients -= 1
|
self.parent_interface.clients -= 1
|
||||||
|
if hasattr(RNS.Transport, "owner") and RNS.Transport.owner != None:
|
||||||
RNS.Transport.owner._should_persist_data()
|
RNS.Transport.owner._should_persist_data()
|
||||||
|
|
||||||
if nowarning == False:
|
if nowarning == False:
|
||||||
@@ -330,6 +342,8 @@ 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
|
||||||
|
if hasattr(self, "_force_bitrate"):
|
||||||
|
spawned_interface._force_bitrate = self._force_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)
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class KISS():
|
|||||||
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_BT_CTRL = 0x46
|
||||||
CMD_PLATFORM = 0x48
|
CMD_PLATFORM = 0x48
|
||||||
CMD_MCU = 0x49
|
CMD_MCU = 0x49
|
||||||
CMD_FW_VERSION = 0x50
|
CMD_FW_VERSION = 0x50
|
||||||
@@ -91,7 +92,7 @@ class RNodeInterface(Interface):
|
|||||||
MAX_CHUNK = 32768
|
MAX_CHUNK = 32768
|
||||||
|
|
||||||
FREQ_MIN = 137000000
|
FREQ_MIN = 137000000
|
||||||
FREQ_MAX = 1020000000
|
FREQ_MAX = 3000000000
|
||||||
|
|
||||||
RSSI_OFFSET = 157
|
RSSI_OFFSET = 157
|
||||||
|
|
||||||
@@ -102,9 +103,13 @@ class RNodeInterface(Interface):
|
|||||||
|
|
||||||
RECONNECT_WAIT = 5
|
RECONNECT_WAIT = 5
|
||||||
|
|
||||||
|
Q_SNR_MIN_BASE = -9
|
||||||
|
Q_SNR_MAX = 6
|
||||||
|
Q_SNR_STEP = 2
|
||||||
|
|
||||||
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):
|
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("Invalid interface type. The Android-specific RNode interface must be used on Android")
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
if importlib.util.find_spec('serial') != None:
|
if importlib.util.find_spec('serial') != None:
|
||||||
@@ -185,15 +190,15 @@ class RNodeInterface(Interface):
|
|||||||
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
|
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
|
||||||
self.validcfg = False
|
self.validcfg = False
|
||||||
|
|
||||||
if (self.txpower < 0 or self.txpower > 17):
|
if (self.txpower < 0 or self.txpower > 22):
|
||||||
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR)
|
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR)
|
||||||
self.validcfg = False
|
self.validcfg = False
|
||||||
|
|
||||||
if (self.bandwidth < 7800 or self.bandwidth > 500000):
|
if (self.bandwidth < 7800 or self.bandwidth > 1625000):
|
||||||
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR)
|
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR)
|
||||||
self.validcfg = False
|
self.validcfg = False
|
||||||
|
|
||||||
if (self.sf < 7 or self.sf > 12):
|
if (self.sf < 5 or self.sf > 12):
|
||||||
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
|
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
|
||||||
self.validcfg = False
|
self.validcfg = False
|
||||||
|
|
||||||
@@ -671,6 +676,18 @@ 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
|
||||||
|
try:
|
||||||
|
sfs = self.r_sf-7
|
||||||
|
snr = self.r_stat_snr
|
||||||
|
q_snr_min = RNodeInterface.Q_SNR_MIN_BASE-sfs*RNodeInterface.Q_SNR_STEP
|
||||||
|
q_snr_max = RNodeInterface.Q_SNR_MAX
|
||||||
|
q_snr_span = q_snr_max-q_snr_min
|
||||||
|
quality = round(((snr-q_snr_min)/(q_snr_span))*100,1)
|
||||||
|
if quality > 100.0: quality = 100.0
|
||||||
|
if quality < 0.0: quality = 0.0
|
||||||
|
self.r_stat_q = quality
|
||||||
|
except:
|
||||||
|
pass
|
||||||
elif (command == KISS.CMD_ST_ALOCK):
|
elif (command == KISS.CMD_ST_ALOCK):
|
||||||
if (byte == KISS.FESC):
|
if (byte == KISS.FESC):
|
||||||
escape = True
|
escape = True
|
||||||
@@ -782,7 +799,7 @@ class RNodeInterface(Interface):
|
|||||||
else:
|
else:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
RNS.log(str(self)+" serial read timeout", RNS.LOG_DEBUG)
|
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
|
||||||
data_buffer = b""
|
data_buffer = b""
|
||||||
in_frame = False
|
in_frame = False
|
||||||
command = KISS.CMD_UNKNOWN
|
command = KISS.CMD_UNKNOWN
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ class TCPClientInterface(Interface):
|
|||||||
elif platform.system() == "Darwin":
|
elif platform.system() == "Darwin":
|
||||||
self.set_timeouts_osx()
|
self.set_timeouts_osx()
|
||||||
|
|
||||||
|
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
|
||||||
elif target_ip != None and target_port != None:
|
elif target_ip != None and target_port != None:
|
||||||
self.receives = True
|
self.receives = True
|
||||||
self.target_ip = target_ip
|
self.target_ip = target_ip
|
||||||
@@ -200,6 +202,7 @@ class TCPClientInterface(Interface):
|
|||||||
|
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT)
|
self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT)
|
||||||
|
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
self.socket.connect((self.target_ip, self.target_port))
|
self.socket.connect((self.target_ip, self.target_port))
|
||||||
self.socket.settimeout(None)
|
self.socket.settimeout(None)
|
||||||
self.online = True
|
self.online = True
|
||||||
|
|||||||
+126
-29
@@ -112,9 +112,10 @@ class Link:
|
|||||||
link = Link(owner = owner, peer_pub_bytes=data[:Link.ECPUBSIZE//2], peer_sig_pub_bytes=data[Link.ECPUBSIZE//2:Link.ECPUBSIZE])
|
link = Link(owner = owner, peer_pub_bytes=data[:Link.ECPUBSIZE//2], peer_sig_pub_bytes=data[Link.ECPUBSIZE//2:Link.ECPUBSIZE])
|
||||||
link.set_link_id(packet)
|
link.set_link_id(packet)
|
||||||
link.destination = packet.destination
|
link.destination = packet.destination
|
||||||
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops)
|
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE
|
||||||
link.establishment_cost += len(packet.raw)
|
link.establishment_cost += len(packet.raw)
|
||||||
RNS.log("Validating link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE)
|
RNS.log("Validating link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE)
|
||||||
|
RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_EXTREME)
|
||||||
link.handshake()
|
link.handshake()
|
||||||
link.attached_interface = packet.receiving_interface
|
link.attached_interface = packet.receiving_interface
|
||||||
link.prove()
|
link.prove()
|
||||||
@@ -123,7 +124,7 @@ class Link:
|
|||||||
link.last_inbound = time.time()
|
link.last_inbound = time.time()
|
||||||
link.start_watchdog()
|
link.start_watchdog()
|
||||||
|
|
||||||
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_VERBOSE)
|
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_DEBUG)
|
||||||
return link
|
return link
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -132,7 +133,7 @@ class Link:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Invalid link request payload size, dropping request", RNS.LOG_VERBOSE)
|
RNS.log("Invalid link request payload size, dropping request", RNS.LOG_DEBUG)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -149,10 +150,14 @@ class Link:
|
|||||||
self.last_inbound = 0
|
self.last_inbound = 0
|
||||||
self.last_outbound = 0
|
self.last_outbound = 0
|
||||||
self.last_proof = 0
|
self.last_proof = 0
|
||||||
|
self.last_data = 0
|
||||||
self.tx = 0
|
self.tx = 0
|
||||||
self.rx = 0
|
self.rx = 0
|
||||||
self.txbytes = 0
|
self.txbytes = 0
|
||||||
self.rxbytes = 0
|
self.rxbytes = 0
|
||||||
|
self.rssi = None
|
||||||
|
self.snr = None
|
||||||
|
self.q = None
|
||||||
self.traffic_timeout_factor = Link.TRAFFIC_TIMEOUT_FACTOR
|
self.traffic_timeout_factor = Link.TRAFFIC_TIMEOUT_FACTOR
|
||||||
self.keepalive_timeout_factor = Link.KEEPALIVE_TIMEOUT_FACTOR
|
self.keepalive_timeout_factor = Link.KEEPALIVE_TIMEOUT_FACTOR
|
||||||
self.keepalive = Link.KEEPALIVE
|
self.keepalive = Link.KEEPALIVE
|
||||||
@@ -163,16 +168,21 @@ class Link:
|
|||||||
self.type = RNS.Destination.LINK
|
self.type = RNS.Destination.LINK
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
|
self.expected_hops = None
|
||||||
self.attached_interface = None
|
self.attached_interface = None
|
||||||
self.__remote_identity = None
|
self.__remote_identity = None
|
||||||
|
self.__track_phy_stats = False
|
||||||
self._channel = None
|
self._channel = None
|
||||||
|
|
||||||
if self.destination == None:
|
if self.destination == None:
|
||||||
self.initiator = False
|
self.initiator = False
|
||||||
self.prv = X25519PrivateKey.generate()
|
self.prv = X25519PrivateKey.generate()
|
||||||
self.sig_prv = self.owner.identity.sig_prv
|
self.sig_prv = self.owner.identity.sig_prv
|
||||||
else:
|
else:
|
||||||
self.initiator = True
|
self.initiator = True
|
||||||
self.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, RNS.Transport.hops_to(destination.hash))
|
self.expected_hops = RNS.Transport.hops_to(self.destination.hash)
|
||||||
|
self.establishment_timeout = RNS.Reticulum.get_instance().get_first_hop_timeout(destination.hash)
|
||||||
|
self.establishment_timeout += Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, RNS.Transport.hops_to(destination.hash))
|
||||||
self.prv = X25519PrivateKey.generate()
|
self.prv = X25519PrivateKey.generate()
|
||||||
self.sig_prv = Ed25519PrivateKey.generate()
|
self.sig_prv = Ed25519PrivateKey.generate()
|
||||||
|
|
||||||
@@ -208,6 +218,7 @@ class Link:
|
|||||||
self.packet.send()
|
self.packet.send()
|
||||||
self.had_outbound()
|
self.had_outbound()
|
||||||
RNS.log("Link request "+RNS.prettyhexrep(self.link_id)+" sent to "+str(self.destination), RNS.LOG_DEBUG)
|
RNS.log("Link request "+RNS.prettyhexrep(self.link_id)+" sent to "+str(self.destination), RNS.LOG_DEBUG)
|
||||||
|
RNS.log(f"Establishment timeout is {RNS.prettytime(self.establishment_timeout)} for link request "+RNS.prettyhexrep(self.link_id), RNS.LOG_EXTREME)
|
||||||
|
|
||||||
|
|
||||||
def load_peer(self, peer_pub_bytes, peer_sig_pub_bytes):
|
def load_peer(self, peer_pub_bytes, peer_sig_pub_bytes):
|
||||||
@@ -345,7 +356,7 @@ class Link:
|
|||||||
packed_request = umsgpack.packb(unpacked_request)
|
packed_request = umsgpack.packb(unpacked_request)
|
||||||
|
|
||||||
if timeout == None:
|
if timeout == None:
|
||||||
timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME/4.0
|
timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME*1.125
|
||||||
|
|
||||||
if len(packed_request) <= Link.MDU:
|
if len(packed_request) <= Link.MDU:
|
||||||
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
|
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
|
||||||
@@ -385,6 +396,7 @@ class Link:
|
|||||||
try:
|
try:
|
||||||
measured_rtt = time.time() - self.request_time
|
measured_rtt = time.time() - self.request_time
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
rtt = umsgpack.unpackb(plaintext)
|
rtt = umsgpack.unpackb(plaintext)
|
||||||
self.rtt = max(measured_rtt, rtt)
|
self.rtt = max(measured_rtt, rtt)
|
||||||
self.status = Link.ACTIVE
|
self.status = Link.ACTIVE
|
||||||
@@ -403,6 +415,38 @@ class Link:
|
|||||||
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
self.teardown()
|
self.teardown()
|
||||||
|
|
||||||
|
def track_phy_stats(self, track):
|
||||||
|
"""
|
||||||
|
You can enable physical layer statistics on a per-link basis. If this is enabled,
|
||||||
|
and the link is running over an interface that supports reporting physical layer
|
||||||
|
statistics, you will be able to retrieve stats such as *RSSI*, *SNR* and physical
|
||||||
|
*Link Quality* for the link.
|
||||||
|
|
||||||
|
:param track: Whether or not to keep track of physical layer statistics. Value must be ``True`` or ``False``.
|
||||||
|
"""
|
||||||
|
if track:
|
||||||
|
self.__track_phy_stats = True
|
||||||
|
else:
|
||||||
|
self.__track_phy_stats = False
|
||||||
|
|
||||||
|
def get_rssi(self):
|
||||||
|
"""
|
||||||
|
:returns: The physical layer *Received Signal Strength Indication* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
|
||||||
|
"""
|
||||||
|
return self.rssi
|
||||||
|
|
||||||
|
def get_snr(self):
|
||||||
|
"""
|
||||||
|
:returns: The physical layer *Signal-to-Noise Ratio* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
|
||||||
|
"""
|
||||||
|
return self.rssi
|
||||||
|
|
||||||
|
def get_q(self):
|
||||||
|
"""
|
||||||
|
:returns: The physical layer *Link Quality* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
|
||||||
|
"""
|
||||||
|
return self.rssi
|
||||||
|
|
||||||
def get_establishment_rate(self):
|
def get_establishment_rate(self):
|
||||||
"""
|
"""
|
||||||
:returns: The data transfer rate at which the link establishment procedure ocurred, in bits per second.
|
:returns: The data transfer rate at which the link establishment procedure ocurred, in bits per second.
|
||||||
@@ -420,7 +464,7 @@ class Link:
|
|||||||
|
|
||||||
def no_inbound_for(self):
|
def no_inbound_for(self):
|
||||||
"""
|
"""
|
||||||
:returns: The time in seconds since last inbound packet on the link.
|
:returns: The time in seconds since last inbound packet on the link. This includes keepalive packets.
|
||||||
"""
|
"""
|
||||||
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(self.last_inbound, activated_at)
|
last_inbound = max(self.last_inbound, activated_at)
|
||||||
@@ -428,13 +472,19 @@ class Link:
|
|||||||
|
|
||||||
def no_outbound_for(self):
|
def no_outbound_for(self):
|
||||||
"""
|
"""
|
||||||
:returns: The time in seconds since last outbound packet on the link.
|
:returns: The time in seconds since last outbound packet on the link. This includes keepalive packets.
|
||||||
"""
|
"""
|
||||||
return time.time() - self.last_outbound
|
return time.time() - self.last_outbound
|
||||||
|
|
||||||
|
def no_data_for(self):
|
||||||
|
"""
|
||||||
|
:returns: The time in seconds since payload data traversed the link. This excludes keepalive packets.
|
||||||
|
"""
|
||||||
|
return time.time() - self.last_data
|
||||||
|
|
||||||
def inactive_for(self):
|
def inactive_for(self):
|
||||||
"""
|
"""
|
||||||
:returns: The time in seconds since activity on the link.
|
:returns: The time in seconds since activity on the link. This includes keepalive packets.
|
||||||
"""
|
"""
|
||||||
return min(self.no_inbound_for(), self.no_outbound_for())
|
return min(self.no_inbound_for(), self.no_outbound_for())
|
||||||
|
|
||||||
@@ -444,8 +494,10 @@ class Link:
|
|||||||
"""
|
"""
|
||||||
return self.__remote_identity
|
return self.__remote_identity
|
||||||
|
|
||||||
def had_outbound(self):
|
def had_outbound(self, is_keepalive=False):
|
||||||
self.last_outbound = time.time()
|
self.last_outbound = time.time()
|
||||||
|
if not is_keepalive:
|
||||||
|
self.last_data = self.last_outbound
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
"""
|
"""
|
||||||
@@ -472,6 +524,7 @@ class Link:
|
|||||||
self.teardown_reason = Link.DESTINATION_CLOSED
|
self.teardown_reason = Link.DESTINATION_CLOSED
|
||||||
else:
|
else:
|
||||||
self.teardown_reason = Link.INITIATOR_CLOSED
|
self.teardown_reason = Link.INITIATOR_CLOSED
|
||||||
|
self.__update_phy_stats(packet)
|
||||||
self.link_closed()
|
self.link_closed()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
@@ -577,10 +630,25 @@ class Link:
|
|||||||
sleep(sleep_time)
|
sleep(sleep_time)
|
||||||
|
|
||||||
|
|
||||||
|
def __update_phy_stats(self, packet, query_shared = True):
|
||||||
|
if self.__track_phy_stats:
|
||||||
|
if query_shared:
|
||||||
|
reticulum = RNS.Reticulum.get_instance()
|
||||||
|
if packet.rssi == None: packet.rssi = reticulum.get_packet_rssi(packet.packet_hash)
|
||||||
|
if packet.snr == None: packet.snr = reticulum.get_packet_snr(packet.packet_hash)
|
||||||
|
if packet.q == None: packet.q = reticulum.get_packet_q(packet.packet_hash)
|
||||||
|
|
||||||
|
if packet.rssi != None:
|
||||||
|
self.rssi = packet.rssi
|
||||||
|
if packet.snr != None:
|
||||||
|
self.snr = packet.snr
|
||||||
|
if packet.q != None:
|
||||||
|
self.q = packet.q
|
||||||
|
|
||||||
def send_keepalive(self):
|
def send_keepalive(self):
|
||||||
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
|
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
|
||||||
keepalive_packet.send()
|
keepalive_packet.send()
|
||||||
self.had_outbound()
|
self.had_outbound(is_keepalive = True)
|
||||||
|
|
||||||
def handle_request(self, request_id, unpacked_request):
|
def handle_request(self, request_id, unpacked_request):
|
||||||
if self.status == Link.ACTIVE:
|
if self.status == Link.ACTIVE:
|
||||||
@@ -631,7 +699,9 @@ class Link:
|
|||||||
remove = pending_request
|
remove = pending_request
|
||||||
try:
|
try:
|
||||||
pending_request.response_size = response_size
|
pending_request.response_size = response_size
|
||||||
pending_request.response_transfer_size = response_transfer_size
|
if pending_request.response_transfer_size == None:
|
||||||
|
pending_request.response_transfer_size = 0
|
||||||
|
pending_request.response_transfer_size += response_transfer_size
|
||||||
pending_request.response_received(response_data)
|
pending_request.response_received(response_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
@@ -684,14 +754,18 @@ class Link:
|
|||||||
RNS.log("Link-associated packet received on unexpected interface! Someone might be trying to manipulate your communication!", RNS.LOG_ERROR)
|
RNS.log("Link-associated packet received on unexpected interface! Someone might be trying to manipulate your communication!", RNS.LOG_ERROR)
|
||||||
else:
|
else:
|
||||||
self.last_inbound = time.time()
|
self.last_inbound = time.time()
|
||||||
|
if packet.context != RNS.Packet.KEEPALIVE:
|
||||||
|
self.last_data = self.last_inbound
|
||||||
self.rx += 1
|
self.rx += 1
|
||||||
self.rxbytes += len(packet.data)
|
self.rxbytes += len(packet.data)
|
||||||
if self.status == Link.STALE:
|
if self.status == Link.STALE:
|
||||||
self.status = Link.ACTIVE
|
self.status = Link.ACTIVE
|
||||||
|
|
||||||
if packet.packet_type == RNS.Packet.DATA:
|
if packet.packet_type == RNS.Packet.DATA:
|
||||||
|
should_query = False
|
||||||
if packet.context == RNS.Packet.NONE:
|
if packet.context == RNS.Packet.NONE:
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
if self.callbacks.packet != None:
|
if self.callbacks.packet != None:
|
||||||
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
|
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
@@ -699,17 +773,22 @@ class Link:
|
|||||||
|
|
||||||
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
|
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
|
||||||
packet.prove()
|
packet.prove()
|
||||||
|
should_query = True
|
||||||
|
|
||||||
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
|
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
|
||||||
if self.destination.callbacks.proof_requested:
|
if self.destination.callbacks.proof_requested:
|
||||||
try:
|
try:
|
||||||
self.destination.callbacks.proof_requested(packet)
|
if self.destination.callbacks.proof_requested(packet):
|
||||||
|
packet.prove()
|
||||||
|
should_query = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
self.__update_phy_stats(packet, query_shared=should_query)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.LINKIDENTIFY:
|
elif packet.context == RNS.Packet.LINKIDENTIFY:
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
|
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
|
||||||
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
|
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
|
||||||
signed_data = self.link_id+public_key
|
signed_data = self.link_id+public_key
|
||||||
@@ -725,35 +804,45 @@ class Link:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.REQUEST:
|
elif packet.context == RNS.Packet.REQUEST:
|
||||||
try:
|
try:
|
||||||
request_id = packet.getTruncatedHash()
|
request_id = packet.getTruncatedHash()
|
||||||
packed_request = self.decrypt(packet.data)
|
packed_request = self.decrypt(packet.data)
|
||||||
|
if packed_request != None:
|
||||||
unpacked_request = umsgpack.unpackb(packed_request)
|
unpacked_request = umsgpack.unpackb(packed_request)
|
||||||
self.handle_request(request_id, unpacked_request)
|
self.handle_request(request_id, unpacked_request)
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.RESPONSE:
|
elif packet.context == RNS.Packet.RESPONSE:
|
||||||
try:
|
try:
|
||||||
packed_response = self.decrypt(packet.data)
|
packed_response = self.decrypt(packet.data)
|
||||||
|
if packed_response != None:
|
||||||
unpacked_response = umsgpack.unpackb(packed_response)
|
unpacked_response = umsgpack.unpackb(packed_response)
|
||||||
request_id = unpacked_response[0]
|
request_id = unpacked_response[0]
|
||||||
response_data = unpacked_response[1]
|
response_data = unpacked_response[1]
|
||||||
transfer_size = len(umsgpack.packb(response_data))-2
|
transfer_size = len(umsgpack.packb(response_data))-2
|
||||||
self.handle_response(request_id, response_data, transfer_size, transfer_size)
|
self.handle_response(request_id, response_data, transfer_size, transfer_size)
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.LRRTT:
|
elif packet.context == RNS.Packet.LRRTT:
|
||||||
if not self.initiator:
|
if not self.initiator:
|
||||||
self.rtt_packet(packet)
|
self.rtt_packet(packet)
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.LINKCLOSE:
|
elif packet.context == RNS.Packet.LINKCLOSE:
|
||||||
self.teardown_packet(packet)
|
self.teardown_packet(packet)
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.RESOURCE_ADV:
|
elif packet.context == RNS.Packet.RESOURCE_ADV:
|
||||||
packet.plaintext = self.decrypt(packet.data)
|
packet.plaintext = self.decrypt(packet.data)
|
||||||
|
if packet.plaintext != None:
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
|
|
||||||
if RNS.ResourceAdvertisement.is_request(packet):
|
if RNS.ResourceAdvertisement.is_request(packet):
|
||||||
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
|
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
|
||||||
@@ -761,10 +850,17 @@ class Link:
|
|||||||
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
|
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
|
||||||
for pending_request in self.pending_requests:
|
for pending_request in self.pending_requests:
|
||||||
if pending_request.request_id == request_id:
|
if pending_request.request_id == request_id:
|
||||||
RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id)
|
response_resource = RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id)
|
||||||
|
if response_resource != None:
|
||||||
|
if pending_request.response_size == None:
|
||||||
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
|
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
|
||||||
pending_request.response_transfer_size = RNS.ResourceAdvertisement.read_transfer_size(packet)
|
if pending_request.response_transfer_size == None:
|
||||||
|
pending_request.response_transfer_size = 0
|
||||||
|
pending_request.response_transfer_size += RNS.ResourceAdvertisement.read_transfer_size(packet)
|
||||||
|
if pending_request.started_at == None:
|
||||||
pending_request.started_at = time.time()
|
pending_request.started_at = time.time()
|
||||||
|
pending_request.response_resource_progress(response_resource)
|
||||||
|
|
||||||
elif self.resource_strategy == Link.ACCEPT_NONE:
|
elif self.resource_strategy == Link.ACCEPT_NONE:
|
||||||
pass
|
pass
|
||||||
elif self.resource_strategy == Link.ACCEPT_APP:
|
elif self.resource_strategy == Link.ACCEPT_APP:
|
||||||
@@ -781,6 +877,8 @@ class Link:
|
|||||||
|
|
||||||
elif packet.context == RNS.Packet.RESOURCE_REQ:
|
elif packet.context == RNS.Packet.RESOURCE_REQ:
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
if ord(plaintext[:1]) == RNS.Resource.HASHMAP_IS_EXHAUSTED:
|
if ord(plaintext[:1]) == RNS.Resource.HASHMAP_IS_EXHAUSTED:
|
||||||
resource_hash = plaintext[1+RNS.Resource.MAPHASH_LEN:RNS.Identity.HASHLENGTH//8+1+RNS.Resource.MAPHASH_LEN]
|
resource_hash = plaintext[1+RNS.Resource.MAPHASH_LEN:RNS.Identity.HASHLENGTH//8+1+RNS.Resource.MAPHASH_LEN]
|
||||||
else:
|
else:
|
||||||
@@ -796,6 +894,8 @@ class Link:
|
|||||||
|
|
||||||
elif packet.context == RNS.Packet.RESOURCE_HMU:
|
elif packet.context == RNS.Packet.RESOURCE_HMU:
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
||||||
for resource in self.incoming_resources:
|
for resource in self.incoming_resources:
|
||||||
if resource_hash == resource.hash:
|
if resource_hash == resource.hash:
|
||||||
@@ -803,6 +903,8 @@ class Link:
|
|||||||
|
|
||||||
elif packet.context == RNS.Packet.RESOURCE_ICL:
|
elif packet.context == RNS.Packet.RESOURCE_ICL:
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
|
self.__update_phy_stats(packet)
|
||||||
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
||||||
for resource in self.incoming_resources:
|
for resource in self.incoming_resources:
|
||||||
if resource_hash == resource.hash:
|
if resource_hash == resource.hash:
|
||||||
@@ -812,7 +914,7 @@ class Link:
|
|||||||
if not self.initiator and packet.data == bytes([0xFF]):
|
if not self.initiator and packet.data == bytes([0xFF]):
|
||||||
keepalive_packet = RNS.Packet(self, bytes([0xFE]), context=RNS.Packet.KEEPALIVE)
|
keepalive_packet = RNS.Packet(self, bytes([0xFE]), context=RNS.Packet.KEEPALIVE)
|
||||||
keepalive_packet.send()
|
keepalive_packet.send()
|
||||||
self.had_outbound()
|
self.had_outbound(is_keepalive = True)
|
||||||
|
|
||||||
|
|
||||||
# TODO: find the most efficient way to allow multiple
|
# TODO: find the most efficient way to allow multiple
|
||||||
@@ -822,26 +924,16 @@ class Link:
|
|||||||
elif packet.context == RNS.Packet.RESOURCE:
|
elif packet.context == RNS.Packet.RESOURCE:
|
||||||
for resource in self.incoming_resources:
|
for resource in self.incoming_resources:
|
||||||
resource.receive_part(packet)
|
resource.receive_part(packet)
|
||||||
|
self.__update_phy_stats(packet)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.CHANNEL:
|
elif packet.context == RNS.Packet.CHANNEL:
|
||||||
if not self._channel:
|
if not self._channel:
|
||||||
RNS.log(f"Channel data received without open channel", RNS.LOG_DEBUG)
|
RNS.log(f"Channel data received without open channel", RNS.LOG_DEBUG)
|
||||||
else:
|
else:
|
||||||
# TODO: Remove packet loss simulator ######
|
|
||||||
# if not hasattr(self, "drop_counter"):
|
|
||||||
# self.drop_counter = 0
|
|
||||||
# self.drop_counter += 1
|
|
||||||
|
|
||||||
# if self.drop_counter%6 == 0:
|
|
||||||
# RNS.log("Dropping channel packet for testing", RNS.LOG_DEBUG)
|
|
||||||
# else:
|
|
||||||
# packet.prove()
|
|
||||||
# plaintext = self.decrypt(packet.data)
|
|
||||||
# self._channel._receive(plaintext)
|
|
||||||
############################################
|
|
||||||
|
|
||||||
packet.prove()
|
packet.prove()
|
||||||
plaintext = self.decrypt(packet.data)
|
plaintext = self.decrypt(packet.data)
|
||||||
|
if plaintext != None:
|
||||||
|
self.__update_phy_stats(packet)
|
||||||
self._channel._receive(plaintext)
|
self._channel._receive(plaintext)
|
||||||
|
|
||||||
elif packet.packet_type == RNS.Packet.PROOF:
|
elif packet.packet_type == RNS.Packet.PROOF:
|
||||||
@@ -850,6 +942,7 @@ class Link:
|
|||||||
for resource in self.outgoing_resources:
|
for resource in self.outgoing_resources:
|
||||||
if resource_hash == resource.hash:
|
if resource_hash == resource.hash:
|
||||||
resource.validate_proof(packet.data)
|
resource.validate_proof(packet.data)
|
||||||
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
|
|
||||||
self.watchdog_lock = False
|
self.watchdog_lock = False
|
||||||
|
|
||||||
@@ -879,6 +972,7 @@ class Link:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def sign(self, message):
|
def sign(self, message):
|
||||||
@@ -1059,6 +1153,7 @@ class RequestReceipt():
|
|||||||
def request_resource_concluded(self, resource):
|
def request_resource_concluded(self, resource):
|
||||||
if resource.status == RNS.Resource.COMPLETE:
|
if resource.status == RNS.Resource.COMPLETE:
|
||||||
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
|
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
|
||||||
|
if self.started_at == None:
|
||||||
self.started_at = time.time()
|
self.started_at = time.time()
|
||||||
self.status = RequestReceipt.DELIVERED
|
self.status = RequestReceipt.DELIVERED
|
||||||
self.__resource_response_timeout = time.time()+self.timeout
|
self.__resource_response_timeout = time.time()+self.timeout
|
||||||
@@ -1100,9 +1195,11 @@ class RequestReceipt():
|
|||||||
|
|
||||||
|
|
||||||
def response_resource_progress(self, resource):
|
def response_resource_progress(self, resource):
|
||||||
|
if resource != None:
|
||||||
if not self.status == RequestReceipt.FAILED:
|
if not self.status == RequestReceipt.FAILED:
|
||||||
self.status = RequestReceipt.RECEIVING
|
self.status = RequestReceipt.RECEIVING
|
||||||
if self.packet_receipt != None:
|
if self.packet_receipt != None:
|
||||||
|
if self.packet_receipt.status != RNS.PacketReceipt.DELIVERED:
|
||||||
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
|
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
|
||||||
self.packet_receipt.proved = True
|
self.packet_receipt.proved = True
|
||||||
self.packet_receipt.concluded_at = time.time()
|
self.packet_receipt.concluded_at = time.time()
|
||||||
|
|||||||
+3
-2
@@ -138,6 +138,7 @@ class Packet:
|
|||||||
self.receiving_interface = None
|
self.receiving_interface = None
|
||||||
self.rssi = None
|
self.rssi = None
|
||||||
self.snr = None
|
self.snr = None
|
||||||
|
self.q = None
|
||||||
|
|
||||||
def get_packed_flags(self):
|
def get_packed_flags(self):
|
||||||
if self.context == Packet.LRPROOF:
|
if self.context == Packet.LRPROOF:
|
||||||
@@ -370,8 +371,8 @@ class PacketReceipt:
|
|||||||
if packet.destination.type == RNS.Destination.LINK:
|
if packet.destination.type == RNS.Destination.LINK:
|
||||||
self.timeout = packet.destination.rtt * packet.destination.traffic_timeout_factor
|
self.timeout = packet.destination.rtt * packet.destination.traffic_timeout_factor
|
||||||
else:
|
else:
|
||||||
self.timeout = Packet.TIMEOUT_PER_HOP * RNS.Transport.hops_to(self.destination.hash)
|
self.timeout = RNS.Reticulum.get_instance().get_first_hop_timeout(self.destination.hash)
|
||||||
|
self.timeout += Packet.TIMEOUT_PER_HOP * RNS.Transport.hops_to(self.destination.hash)
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
+52
-22
@@ -25,7 +25,9 @@ import os
|
|||||||
import bz2
|
import bz2
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
|
from threading import Lock
|
||||||
from .vendor import umsgpack as umsgpack
|
from .vendor import umsgpack as umsgpack
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ class Resource:
|
|||||||
WINDOW = 4
|
WINDOW = 4
|
||||||
|
|
||||||
# Absolute minimum window size during transfer
|
# Absolute minimum window size during transfer
|
||||||
WINDOW_MIN = 1
|
WINDOW_MIN = 2
|
||||||
|
|
||||||
# The maximum window size for transfers on slow links
|
# The maximum window size for transfers on slow links
|
||||||
WINDOW_MAX_SLOW = 10
|
WINDOW_MAX_SLOW = 10
|
||||||
@@ -103,9 +105,10 @@ class Resource:
|
|||||||
|
|
||||||
PART_TIMEOUT_FACTOR = 4
|
PART_TIMEOUT_FACTOR = 4
|
||||||
PART_TIMEOUT_FACTOR_AFTER_RTT = 2
|
PART_TIMEOUT_FACTOR_AFTER_RTT = 2
|
||||||
MAX_RETRIES = 8
|
MAX_RETRIES = 16
|
||||||
MAX_ADV_RETRIES = 4
|
MAX_ADV_RETRIES = 4
|
||||||
SENDER_GRACE_TIME = 10
|
SENDER_GRACE_TIME = 10.0
|
||||||
|
PROCESSING_GRACE = 1.0
|
||||||
RETRY_GRACE_TIME = 0.25
|
RETRY_GRACE_TIME = 0.25
|
||||||
PER_RETRY_DELAY = 0.5
|
PER_RETRY_DELAY = 0.5
|
||||||
|
|
||||||
@@ -167,15 +170,13 @@ class Resource:
|
|||||||
resource.hashmap = [None] * resource.total_parts
|
resource.hashmap = [None] * resource.total_parts
|
||||||
resource.hashmap_height = 0
|
resource.hashmap_height = 0
|
||||||
resource.waiting_for_hmu = False
|
resource.waiting_for_hmu = False
|
||||||
|
|
||||||
resource.receiving_part = False
|
resource.receiving_part = False
|
||||||
|
resource.consecutive_completed_height = -1
|
||||||
resource.consecutive_completed_height = 0
|
|
||||||
|
|
||||||
if not resource.link.has_incoming_resource(resource):
|
if not resource.link.has_incoming_resource(resource):
|
||||||
resource.link.register_incoming_resource(resource)
|
resource.link.register_incoming_resource(resource)
|
||||||
|
|
||||||
RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
|
RNS.log(f"Accepting resource advertisement for {RNS.prettyhexrep(resource.hash)}. Transfer size is {RNS.prettysize(resource.size)} in {resource.total_parts} parts.", RNS.LOG_DEBUG)
|
||||||
if resource.link.callbacks.resource_started != None:
|
if resource.link.callbacks.resource_started != None:
|
||||||
try:
|
try:
|
||||||
resource.link.callbacks.resource_started(resource)
|
resource.link.callbacks.resource_started(resource)
|
||||||
@@ -204,8 +205,18 @@ class Resource:
|
|||||||
resource_data = None
|
resource_data = None
|
||||||
self.assembly_lock = False
|
self.assembly_lock = False
|
||||||
|
|
||||||
|
if data != None:
|
||||||
|
if not hasattr(data, "read") and len(data) > Resource.MAX_EFFICIENT_SIZE:
|
||||||
|
original_data = data
|
||||||
|
data_size = len(original_data)
|
||||||
|
data = tempfile.TemporaryFile()
|
||||||
|
data.write(original_data)
|
||||||
|
del original_data
|
||||||
|
|
||||||
if hasattr(data, "read"):
|
if hasattr(data, "read"):
|
||||||
|
if data_size == None:
|
||||||
data_size = os.stat(data.name).st_size
|
data_size = os.stat(data.name).st_size
|
||||||
|
|
||||||
self.total_size = data_size
|
self.total_size = data_size
|
||||||
self.grand_total_parts = math.ceil(data_size/Resource.SDU)
|
self.grand_total_parts = math.ceil(data_size/Resource.SDU)
|
||||||
|
|
||||||
@@ -279,7 +290,7 @@ class Resource:
|
|||||||
self.uncompressed_data = data
|
self.uncompressed_data = data
|
||||||
|
|
||||||
compression_began = time.time()
|
compression_began = time.time()
|
||||||
if (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE):
|
if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE):
|
||||||
RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
|
RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
|
||||||
self.compressed_data = bz2.compress(self.uncompressed_data)
|
self.compressed_data = bz2.compress(self.uncompressed_data)
|
||||||
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
|
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
|
||||||
@@ -366,7 +377,8 @@ class Resource:
|
|||||||
if advertise:
|
if advertise:
|
||||||
self.advertise()
|
self.advertise()
|
||||||
else:
|
else:
|
||||||
pass
|
self.receive_lock = Lock()
|
||||||
|
|
||||||
|
|
||||||
def hashmap_update_packet(self, plaintext):
|
def hashmap_update_packet(self, plaintext):
|
||||||
if not self.status == Resource.FAILED:
|
if not self.status == Resource.FAILED:
|
||||||
@@ -440,7 +452,7 @@ class Resource:
|
|||||||
sleep_time = None
|
sleep_time = None
|
||||||
|
|
||||||
if self.status == Resource.ADVERTISED:
|
if self.status == Resource.ADVERTISED:
|
||||||
sleep_time = (self.adv_sent+self.timeout)-time.time()
|
sleep_time = (self.adv_sent+self.timeout+Resource.PROCESSING_GRACE)-time.time()
|
||||||
if sleep_time < 0:
|
if sleep_time < 0:
|
||||||
if self.retries_left <= 0:
|
if self.retries_left <= 0:
|
||||||
RNS.log("Resource transfer timeout after sending advertisement", RNS.LOG_DEBUG)
|
RNS.log("Resource transfer timeout after sending advertisement", RNS.LOG_DEBUG)
|
||||||
@@ -456,7 +468,7 @@ class Resource:
|
|||||||
self.adv_sent = self.last_activity
|
self.adv_sent = self.last_activity
|
||||||
sleep_time = 0.001
|
sleep_time = 0.001
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not resend advertisement packet, cancelling resource", RNS.LOG_VERBOSE)
|
RNS.log("Could not resend advertisement packet, cancelling resource. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
|
||||||
self.cancel()
|
self.cancel()
|
||||||
|
|
||||||
|
|
||||||
@@ -612,10 +624,26 @@ class Resource:
|
|||||||
self.callback(self)
|
self.callback(self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while executing resource concluded callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while executing resource concluded callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if hasattr(self, "input_file"):
|
||||||
|
if hasattr(self.input_file, "close") and callable(self.input_file.close):
|
||||||
|
self.input_file.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Error while closing resource input file: "+str(e), RNS.LOG_ERROR)
|
||||||
else:
|
else:
|
||||||
# Otherwise we'll recursively create the
|
# Otherwise we'll recursively create the
|
||||||
# next segment of the resource
|
# next segment of the resource
|
||||||
Resource(self.input_file, self.link, callback = self.callback, segment_index = self.segment_index+1, original_hash=self.original_hash, progress_callback = self.__progress_callback)
|
Resource(
|
||||||
|
self.input_file, self.link,
|
||||||
|
callback = self.callback,
|
||||||
|
segment_index = self.segment_index+1,
|
||||||
|
original_hash=self.original_hash,
|
||||||
|
progress_callback = self.__progress_callback,
|
||||||
|
request_id = self.request_id,
|
||||||
|
is_response = self.is_response,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -623,8 +651,7 @@ class Resource:
|
|||||||
|
|
||||||
|
|
||||||
def receive_part(self, packet):
|
def receive_part(self, packet):
|
||||||
while self.receiving_part:
|
with self.receive_lock:
|
||||||
sleep(0.001)
|
|
||||||
|
|
||||||
self.receiving_part = True
|
self.receiving_part = True
|
||||||
self.last_activity = time.time()
|
self.last_activity = time.time()
|
||||||
@@ -658,8 +685,9 @@ class Resource:
|
|||||||
part_data = packet.data
|
part_data = packet.data
|
||||||
part_hash = self.get_map_hash(part_data)
|
part_hash = self.get_map_hash(part_data)
|
||||||
|
|
||||||
i = self.consecutive_completed_height
|
consecutive_index = self.consecutive_completed_height if self.consecutive_completed_height >= 0 else 0
|
||||||
for map_hash in self.hashmap[self.consecutive_completed_height:self.consecutive_completed_height+self.window]:
|
i = consecutive_index
|
||||||
|
for map_hash in self.hashmap[consecutive_index:consecutive_index+self.window]:
|
||||||
if map_hash == part_hash:
|
if map_hash == part_hash:
|
||||||
if self.parts[i] == None:
|
if self.parts[i] == None:
|
||||||
|
|
||||||
@@ -728,11 +756,11 @@ class Resource:
|
|||||||
hashmap_exhausted = Resource.HASHMAP_IS_NOT_EXHAUSTED
|
hashmap_exhausted = Resource.HASHMAP_IS_NOT_EXHAUSTED
|
||||||
requested_hashes = b""
|
requested_hashes = b""
|
||||||
|
|
||||||
offset = (1 if self.consecutive_completed_height > 0 else 0)
|
i = 0; pn = self.consecutive_completed_height+1
|
||||||
i = 0; pn = self.consecutive_completed_height+offset
|
|
||||||
search_start = pn
|
search_start = pn
|
||||||
|
search_size = self.window
|
||||||
|
|
||||||
for part in self.parts[search_start:search_start+self.window]:
|
for part in self.parts[search_start:search_start+search_size]:
|
||||||
if part == None:
|
if part == None:
|
||||||
part_hash = self.hashmap[pn]
|
part_hash = self.hashmap[pn]
|
||||||
if part_hash != None:
|
if part_hash != None:
|
||||||
@@ -752,7 +780,6 @@ class Resource:
|
|||||||
hmu_part += last_map_hash
|
hmu_part += last_map_hash
|
||||||
self.waiting_for_hmu = True
|
self.waiting_for_hmu = True
|
||||||
|
|
||||||
requested_data = b""
|
|
||||||
request_data = hmu_part + self.hash + requested_hashes
|
request_data = hmu_part + self.hash + requested_hashes
|
||||||
request_packet = RNS.Packet(self.link, request_data, context = RNS.Packet.RESOURCE_REQ)
|
request_packet = RNS.Packet(self.link, request_data, context = RNS.Packet.RESOURCE_REQ)
|
||||||
|
|
||||||
@@ -908,8 +935,7 @@ class Resource:
|
|||||||
else:
|
else:
|
||||||
self.progress_total_parts = float(self.total_parts)
|
self.progress_total_parts = float(self.total_parts)
|
||||||
|
|
||||||
|
progress = min(1.0, self.processed_parts / self.progress_total_parts)
|
||||||
progress = self.processed_parts / self.progress_total_parts
|
|
||||||
return progress
|
return progress
|
||||||
|
|
||||||
def get_transfer_size(self):
|
def get_transfer_size(self):
|
||||||
@@ -997,6 +1023,7 @@ class ResourceAdvertisement:
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, resource=None, request_id=None, is_response=False):
|
def __init__(self, resource=None, request_id=None, is_response=False):
|
||||||
|
self.link = None
|
||||||
if resource != None:
|
if resource != None:
|
||||||
self.t = resource.size # Transfer size
|
self.t = resource.size # Transfer size
|
||||||
self.d = resource.total_size # Total uncompressed data size
|
self.d = resource.total_size # Total uncompressed data size
|
||||||
@@ -1043,6 +1070,9 @@ class ResourceAdvertisement:
|
|||||||
def is_compressed(self):
|
def is_compressed(self):
|
||||||
return self.c
|
return self.c
|
||||||
|
|
||||||
|
def get_link(self):
|
||||||
|
return self.link
|
||||||
|
|
||||||
def pack(self, segment=0):
|
def pack(self, segment=0):
|
||||||
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
|
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
|
||||||
hashmap_end = min((segment+1)*(ResourceAdvertisement.HASHMAP_MAX_LEN), self.n)
|
hashmap_end = min((segment+1)*(ResourceAdvertisement.HASHMAP_MAX_LEN), self.n)
|
||||||
|
|||||||
+79
-9
@@ -79,7 +79,7 @@ class Reticulum:
|
|||||||
MTU = 500
|
MTU = 500
|
||||||
"""
|
"""
|
||||||
The MTU that Reticulum adheres to, and will expect other peers to
|
The MTU that Reticulum adheres to, and will expect other peers to
|
||||||
adhere to. By default, the MTU is 507 bytes. In custom RNS network
|
adhere to. By default, the MTU is 500 bytes. In custom RNS network
|
||||||
implementations, it is possible to change this value, but doing so will
|
implementations, it is possible to change this value, but doing so will
|
||||||
completely break compatibility with all other RNS networks. An identical
|
completely break compatibility with all other RNS networks. An identical
|
||||||
MTU is a prerequisite for peers to communicate in the same network.
|
MTU is a prerequisite for peers to communicate in the same network.
|
||||||
@@ -106,17 +106,19 @@ class Reticulum:
|
|||||||
it will eventually be dropped.
|
it will eventually be dropped.
|
||||||
|
|
||||||
This value will be applied by default to all created interfaces,
|
This value will be applied by default to all created interfaces,
|
||||||
but it can be configured individually on a per-interface basis.
|
but it can be configured individually on a per-interface basis. In
|
||||||
|
general, the global default setting should not be changed, and any
|
||||||
|
alterations should be made on a per-interface basis instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MINIMUM_BITRATE = 500
|
MINIMUM_BITRATE = 5
|
||||||
|
"""
|
||||||
|
Minimum bitrate required across a medium for Reticulum to be able
|
||||||
|
to successfully establish links. Currently 5 bits per second.
|
||||||
|
"""
|
||||||
|
|
||||||
# TODO: To reach the 300bps level without unreasonably impacting
|
# TODO: Let Reticulum somehow continously build a map of per-hop
|
||||||
# performance on faster links, we need a mechanism for setting
|
# latencies and use this map for global timeout calculation.
|
||||||
# this value more intelligently. One option could be inferring it
|
|
||||||
# from interface speed, but a better general approach would most
|
|
||||||
# probably be to let Reticulum somehow continously build a map of
|
|
||||||
# per-hop latencies and use this map for the timeout calculation.
|
|
||||||
DEFAULT_PER_HOP_TIMEOUT = 6
|
DEFAULT_PER_HOP_TIMEOUT = 6
|
||||||
|
|
||||||
# Length of truncated hashes in bits.
|
# Length of truncated hashes in bits.
|
||||||
@@ -146,6 +148,8 @@ class Reticulum:
|
|||||||
storagepath = ""
|
storagepath = ""
|
||||||
cachepath = ""
|
cachepath = ""
|
||||||
|
|
||||||
|
__instance = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def exit_handler():
|
def exit_handler():
|
||||||
# This exit handler is called whenever Reticulum is asked to
|
# This exit handler is called whenever Reticulum is asked to
|
||||||
@@ -168,6 +172,13 @@ class Reticulum:
|
|||||||
RNS.exit()
|
RNS.exit()
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_instance():
|
||||||
|
"""
|
||||||
|
Return the currently running Reticulum instance
|
||||||
|
"""
|
||||||
|
return Reticulum.__instance
|
||||||
|
|
||||||
def __init__(self,configdir=None, loglevel=None, logdest=None, verbosity=None):
|
def __init__(self,configdir=None, loglevel=None, logdest=None, verbosity=None):
|
||||||
"""
|
"""
|
||||||
Initialises and starts a Reticulum instance. This must be
|
Initialises and starts a Reticulum instance. This must be
|
||||||
@@ -177,6 +188,11 @@ class Reticulum:
|
|||||||
:param configdir: Full path to a Reticulum configuration directory.
|
:param configdir: Full path to a Reticulum configuration directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if Reticulum.__instance != None:
|
||||||
|
raise OSError("Attempt to reinitialise Reticulum, when it was already running")
|
||||||
|
else:
|
||||||
|
Reticulum.__instance = self
|
||||||
|
|
||||||
RNS.vendor.platformutils.platform_checks()
|
RNS.vendor.platformutils.platform_checks()
|
||||||
|
|
||||||
if configdir != None:
|
if configdir != None:
|
||||||
@@ -303,6 +319,10 @@ class Reticulum:
|
|||||||
self.local_interface_port
|
self.local_interface_port
|
||||||
)
|
)
|
||||||
interface.OUT = True
|
interface.OUT = True
|
||||||
|
if hasattr(Reticulum, "_force_shared_instance_bitrate"):
|
||||||
|
interface.bitrate = Reticulum._force_shared_instance_bitrate
|
||||||
|
interface._force_bitrate = Reticulum._force_shared_instance_bitrate
|
||||||
|
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
|
||||||
RNS.Transport.interfaces.append(interface)
|
RNS.Transport.interfaces.append(interface)
|
||||||
|
|
||||||
self.is_shared_instance = True
|
self.is_shared_instance = True
|
||||||
@@ -317,6 +337,10 @@ class Reticulum:
|
|||||||
self.local_interface_port)
|
self.local_interface_port)
|
||||||
interface.target_port = self.local_interface_port
|
interface.target_port = self.local_interface_port
|
||||||
interface.OUT = True
|
interface.OUT = True
|
||||||
|
if hasattr(Reticulum, "_force_shared_instance_bitrate"):
|
||||||
|
interface.bitrate = Reticulum._force_shared_instance_bitrate
|
||||||
|
interface._force_bitrate = True
|
||||||
|
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
|
||||||
RNS.Transport.interfaces.append(interface)
|
RNS.Transport.interfaces.append(interface)
|
||||||
self.is_shared_instance = False
|
self.is_shared_instance = False
|
||||||
self.is_standalone_instance = False
|
self.is_standalone_instance = False
|
||||||
@@ -376,6 +400,9 @@ class Reticulum:
|
|||||||
v = self.config["reticulum"].as_bool(option)
|
v = self.config["reticulum"].as_bool(option)
|
||||||
if v == True:
|
if v == True:
|
||||||
Reticulum.__allow_probes = True
|
Reticulum.__allow_probes = True
|
||||||
|
if option == "force_shared_instance_bitrate":
|
||||||
|
v = self.config["reticulum"].as_int(option)
|
||||||
|
Reticulum._force_shared_instance_bitrate = v
|
||||||
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:
|
||||||
@@ -513,6 +540,7 @@ class Reticulum:
|
|||||||
group_id = c["group_id"] if "group_id" in c else None
|
group_id = c["group_id"] if "group_id" in c else None
|
||||||
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
|
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
|
||||||
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
|
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
|
||||||
|
multicast_address_type = c["multicast_address_type"] if "multicast_address_type" in c else None
|
||||||
data_port = int(c["data_port"]) if "data_port" in c else None
|
data_port = int(c["data_port"]) if "data_port" in c else None
|
||||||
allowed_interfaces = c.as_list("devices") if "devices" in c else None
|
allowed_interfaces = c.as_list("devices") if "devices" in c else None
|
||||||
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
|
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
|
||||||
@@ -523,6 +551,7 @@ class Reticulum:
|
|||||||
group_id,
|
group_id,
|
||||||
discovery_scope,
|
discovery_scope,
|
||||||
discovery_port,
|
discovery_port,
|
||||||
|
multicast_address_type,
|
||||||
data_port,
|
data_port,
|
||||||
allowed_interfaces,
|
allowed_interfaces,
|
||||||
ignored_interfaces
|
ignored_interfaces
|
||||||
@@ -1074,12 +1103,18 @@ class Reticulum:
|
|||||||
if path == "next_hop":
|
if path == "next_hop":
|
||||||
rpc_connection.send(self.get_next_hop(call["destination_hash"]))
|
rpc_connection.send(self.get_next_hop(call["destination_hash"]))
|
||||||
|
|
||||||
|
if path == "first_hop_timeout":
|
||||||
|
rpc_connection.send(self.get_first_hop_timeout(call["destination_hash"]))
|
||||||
|
|
||||||
if path == "packet_rssi":
|
if path == "packet_rssi":
|
||||||
rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
|
rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
|
||||||
|
|
||||||
if path == "packet_snr":
|
if path == "packet_snr":
|
||||||
rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
|
rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
|
||||||
|
|
||||||
|
if path == "packet_q":
|
||||||
|
rpc_connection.send(self.get_packet_q(call["packet_hash"]))
|
||||||
|
|
||||||
if "drop" in call:
|
if "drop" in call:
|
||||||
path = call["drop"]
|
path = call["drop"]
|
||||||
|
|
||||||
@@ -1289,6 +1324,26 @@ class Reticulum:
|
|||||||
else:
|
else:
|
||||||
return str(RNS.Transport.next_hop_interface(destination))
|
return str(RNS.Transport.next_hop_interface(destination))
|
||||||
|
|
||||||
|
def get_first_hop_timeout(self, destination):
|
||||||
|
if self.is_connected_to_shared_instance:
|
||||||
|
try:
|
||||||
|
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||||
|
rpc_connection.send({"get": "first_hop_timeout", "destination_hash": destination})
|
||||||
|
response = rpc_connection.recv()
|
||||||
|
|
||||||
|
if self.is_connected_to_shared_instance and hasattr(self, "_force_shared_instance_bitrate") and self._force_shared_instance_bitrate:
|
||||||
|
simulated_latency = ((1/self._force_shared_instance_bitrate)*8)*RNS.Reticulum.MTU
|
||||||
|
RNS.log("Adding simulated latency of "+RNS.prettytime(simulated_latency)+" to first hop timeout", RNS.LOG_DEBUG)
|
||||||
|
response += simulated_latency
|
||||||
|
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("An error occurred while getting first hop timeout from shared instance: "+str(e), RNS.LOG_ERROR)
|
||||||
|
return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||||
|
|
||||||
|
else:
|
||||||
|
return RNS.Transport.first_hop_timeout(destination)
|
||||||
|
|
||||||
def get_next_hop(self, destination):
|
def get_next_hop(self, destination):
|
||||||
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)
|
||||||
@@ -1328,6 +1383,21 @@ class Reticulum:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_packet_q(self, packet_hash):
|
||||||
|
if self.is_connected_to_shared_instance:
|
||||||
|
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||||
|
rpc_connection.send({"get": "packet_q", "packet_hash": packet_hash})
|
||||||
|
response = rpc_connection.recv()
|
||||||
|
return response
|
||||||
|
|
||||||
|
else:
|
||||||
|
for entry in RNS.Transport.local_client_q_cache:
|
||||||
|
if entry[0] == packet_hash:
|
||||||
|
return entry[1]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def should_use_implicit_proof():
|
def should_use_implicit_proof():
|
||||||
"""
|
"""
|
||||||
|
|||||||
+259
-47
@@ -64,15 +64,21 @@ class Transport:
|
|||||||
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
|
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
|
||||||
|
|
||||||
PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds
|
PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds
|
||||||
PATH_REQUEST_GRACE = 0.35 # Grace time before a path announcement is made, allows directly reachable peers to respond first
|
PATH_REQUEST_GRACE = 0.4 # Grace time before a path announcement is made, allows directly reachable peers to respond first
|
||||||
PATH_REQUEST_RW = 2 # Path request random window
|
PATH_REQUEST_RG = 0.6 # Extra grace time for roaming-mode interfaces to allow more suitable peers to respond first
|
||||||
PATH_REQUEST_MI = 5 # Minimum interval in seconds for automated path requests
|
PATH_REQUEST_MI = 20 # Minimum interval in seconds for automated path requests
|
||||||
|
|
||||||
|
STATE_UNKNOWN = 0x00
|
||||||
|
STATE_UNRESPONSIVE = 0x01
|
||||||
|
STATE_RESPONSIVE = 0x02
|
||||||
|
|
||||||
LINK_TIMEOUT = RNS.Link.STALE_TIME * 1.25
|
LINK_TIMEOUT = RNS.Link.STALE_TIME * 1.25
|
||||||
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after 30 minutes
|
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after 30 minutes
|
||||||
DESTINATION_TIMEOUT = 60*60*24*7 # Destination table entries are removed if unused for one week
|
DESTINATION_TIMEOUT = 60*60*24*7 # Destination table entries are removed if unused for one week
|
||||||
MAX_RECEIPTS = 1024 # Maximum number of receipts to keep track of
|
MAX_RECEIPTS = 1024 # Maximum number of receipts to keep track of
|
||||||
MAX_RATE_TIMESTAMPS = 16 # Maximum number of announce timestamps to keep per destination
|
MAX_RATE_TIMESTAMPS = 16 # Maximum number of announce timestamps to keep per destination
|
||||||
|
PERSIST_RANDOM_BLOBS = 32 # Maximum number of random blobs per destination to persist to disk
|
||||||
|
MAX_RANDOM_BLOBS = 64 # Maximum number of random blobs per destination to keep in memory
|
||||||
|
|
||||||
interfaces = [] # All active interfaces
|
interfaces = [] # All active interfaces
|
||||||
destinations = [] # All active destinations
|
destinations = [] # All active destinations
|
||||||
@@ -94,6 +100,7 @@ class Transport:
|
|||||||
tunnels = {} # A table storing tunnels to other transport instances
|
tunnels = {} # A table storing tunnels to other transport instances
|
||||||
announce_rate_table = {} # A table for keeping track of announce rates
|
announce_rate_table = {} # A table for keeping track of announce rates
|
||||||
path_requests = {} # A table for storing path request timestamps
|
path_requests = {} # A table for storing path request timestamps
|
||||||
|
path_states = {} # A table for keeping track of path states
|
||||||
|
|
||||||
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
||||||
discovery_pr_tags = [] # A table for keeping track of tagged path requests
|
discovery_pr_tags = [] # A table for keeping track of tagged path requests
|
||||||
@@ -111,6 +118,7 @@ class Transport:
|
|||||||
|
|
||||||
local_client_rssi_cache = []
|
local_client_rssi_cache = []
|
||||||
local_client_snr_cache = []
|
local_client_snr_cache = []
|
||||||
|
local_client_q_cache = []
|
||||||
LOCAL_CLIENT_CACHE_MAXSIZE = 512
|
LOCAL_CLIENT_CACHE_MAXSIZE = 512
|
||||||
|
|
||||||
pending_local_path_requests = {}
|
pending_local_path_requests = {}
|
||||||
@@ -299,7 +307,8 @@ class Transport:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def jobs():
|
def jobs():
|
||||||
outgoing = []
|
outgoing = []
|
||||||
path_requests = []
|
path_requests = {}
|
||||||
|
blocked_if = None
|
||||||
Transport.jobs_running = True
|
Transport.jobs_running = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -327,7 +336,8 @@ class Transport:
|
|||||||
if time.time() - last_path_request > Transport.PATH_REQUEST_MI:
|
if time.time() - last_path_request > Transport.PATH_REQUEST_MI:
|
||||||
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link.destination.hash)+" since an attempted link was never established", RNS.LOG_DEBUG)
|
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link.destination.hash)+" since an attempted link was never established", RNS.LOG_DEBUG)
|
||||||
if not link.destination.hash in path_requests:
|
if not link.destination.hash in path_requests:
|
||||||
path_requests.append(link.destination.hash)
|
blocked_if = None
|
||||||
|
path_requests[link.destination.hash] = blocked_if
|
||||||
|
|
||||||
Transport.pending_links.remove(link)
|
Transport.pending_links.remove(link)
|
||||||
|
|
||||||
@@ -422,6 +432,12 @@ class Transport:
|
|||||||
Transport.discovery_pr_tags = Transport.discovery_pr_tags[len(Transport.discovery_pr_tags)-Transport.max_pr_tags:len(Transport.discovery_pr_tags)-1]
|
Transport.discovery_pr_tags = Transport.discovery_pr_tags[len(Transport.discovery_pr_tags)-Transport.max_pr_tags:len(Transport.discovery_pr_tags)-1]
|
||||||
|
|
||||||
if time.time() > Transport.tables_last_culled + Transport.tables_cull_interval:
|
if time.time() > Transport.tables_last_culled + Transport.tables_cull_interval:
|
||||||
|
# Remove unneeded path state entries
|
||||||
|
stale_path_states = []
|
||||||
|
for destination_hash in Transport.path_states:
|
||||||
|
if not destination_hash in Transport.destination_table:
|
||||||
|
stale_path_states.append(destination_hash)
|
||||||
|
|
||||||
# Cull the reverse table according to timeout
|
# Cull the reverse table according to timeout
|
||||||
stale_reverse_entries = []
|
stale_reverse_entries = []
|
||||||
for truncated_packet_hash in Transport.reverse_table:
|
for truncated_packet_hash in Transport.reverse_table:
|
||||||
@@ -470,18 +486,24 @@ class Transport:
|
|||||||
elif not path_request_throttle and Transport.hops_to(link_entry[6]) == 1:
|
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)
|
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
|
path_request_conditions = True
|
||||||
|
blocked_if = link_entry[4]
|
||||||
|
|
||||||
# If the link destination was previously only 1 hop
|
# If the link initiator is only 1 hop away,
|
||||||
# away, this likely means that it was local to one
|
# this likely means that network topology has
|
||||||
# of our interfaces, and that it roamed somewhere else.
|
# changed. In that case, we try to discover a new path,
|
||||||
# In that case, try to discover a new path.
|
# and mark the old one as potentially unresponsive.
|
||||||
elif not path_request_throttle and lr_taken_hops == 1:
|
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)
|
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
|
path_request_conditions = True
|
||||||
|
blocked_if = link_entry[4]
|
||||||
|
|
||||||
|
if RNS.Reticulum.transport_enabled():
|
||||||
|
if hasattr(link_entry[4], "mode") and link_entry[4].mode != RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||||
|
Transport.mark_path_unresponsive(link_entry[6])
|
||||||
|
|
||||||
if path_request_conditions:
|
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[link_entry[6]] = blocked_if
|
||||||
|
|
||||||
if not RNS.Reticulum.transport_enabled():
|
if not RNS.Reticulum.transport_enabled():
|
||||||
# Drop current path if we are not a transport instance, to
|
# Drop current path if we are not a transport instance, to
|
||||||
@@ -549,8 +571,6 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME)
|
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for truncated_packet_hash in stale_reverse_entries:
|
for truncated_packet_hash in stale_reverse_entries:
|
||||||
Transport.reverse_table.pop(truncated_packet_hash)
|
Transport.reverse_table.pop(truncated_packet_hash)
|
||||||
@@ -562,8 +582,6 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME)
|
RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for link_id in stale_links:
|
for link_id in stale_links:
|
||||||
Transport.link_table.pop(link_id)
|
Transport.link_table.pop(link_id)
|
||||||
@@ -608,6 +626,17 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_EXTREME)
|
RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for destination_hash in stale_path_states:
|
||||||
|
Transport.path_states.pop(destination_hash)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i > 0:
|
||||||
|
if i == 1:
|
||||||
|
RNS.log("Removed "+str(i)+" path state entry", RNS.LOG_EXTREME)
|
||||||
|
else:
|
||||||
|
RNS.log("Removed "+str(i)+" path state entries", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
Transport.tables_last_culled = time.time()
|
Transport.tables_last_culled = time.time()
|
||||||
|
|
||||||
if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
|
if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
|
||||||
@@ -629,7 +658,17 @@ class Transport:
|
|||||||
packet.send()
|
packet.send()
|
||||||
|
|
||||||
for destination_hash in path_requests:
|
for destination_hash in path_requests:
|
||||||
|
blocked_if = path_requests[destination_hash]
|
||||||
|
if blocked_if == None:
|
||||||
Transport.request_path(destination_hash)
|
Transport.request_path(destination_hash)
|
||||||
|
else:
|
||||||
|
for interface in Transport.interfaces:
|
||||||
|
if interface != blocked_if:
|
||||||
|
# RNS.log("Transmitting path request on "+str(interface), RNS.LOG_DEBUG)
|
||||||
|
Transport.request_path(destination_hash, on_interface=interface)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# RNS.log("Blocking path request on "+str(interface), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def transmit(interface, raw):
|
def transmit(interface, raw):
|
||||||
@@ -686,6 +725,30 @@ class Transport:
|
|||||||
sent = False
|
sent = False
|
||||||
outbound_time = time.time()
|
outbound_time = time.time()
|
||||||
|
|
||||||
|
generate_receipt = False
|
||||||
|
if (packet.create_receipt == True and
|
||||||
|
# Only generate receipts for DATA packets
|
||||||
|
packet.packet_type == RNS.Packet.DATA and
|
||||||
|
# Don't generate receipts for PLAIN destinations
|
||||||
|
packet.destination.type != RNS.Destination.PLAIN and
|
||||||
|
# Don't generate receipts for link-related packets
|
||||||
|
not (packet.context >= RNS.Packet.KEEPALIVE and packet.context <= RNS.Packet.LRPROOF) and
|
||||||
|
# Don't generate receipts for resource packets
|
||||||
|
not (packet.context >= RNS.Packet.RESOURCE and packet.context <= RNS.Packet.RESOURCE_RCL)):
|
||||||
|
|
||||||
|
generate_receipt = True
|
||||||
|
|
||||||
|
def packet_sent(packet):
|
||||||
|
packet.sent = True
|
||||||
|
packet.sent_at = time.time()
|
||||||
|
|
||||||
|
if generate_receipt:
|
||||||
|
packet.receipt = RNS.PacketReceipt(packet)
|
||||||
|
Transport.receipts.append(packet.receipt)
|
||||||
|
|
||||||
|
# TODO: Enable when caching has been redesigned
|
||||||
|
# Transport.cache(packet)
|
||||||
|
|
||||||
# Check if we have a known path for the destination in the path table
|
# Check if we have a known path for the destination in the path table
|
||||||
if packet.packet_type != RNS.Packet.ANNOUNCE and packet.destination.type != RNS.Destination.PLAIN and packet.destination.type != RNS.Destination.GROUP and packet.destination_hash in Transport.destination_table:
|
if packet.packet_type != RNS.Packet.ANNOUNCE and packet.destination.type != RNS.Destination.PLAIN and packet.destination.type != RNS.Destination.GROUP and packet.destination_hash in Transport.destination_table:
|
||||||
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
||||||
@@ -703,6 +766,7 @@ class Transport:
|
|||||||
new_raw += packet.raw[1:2]
|
new_raw += packet.raw[1:2]
|
||||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||||
new_raw += packet.raw[2:]
|
new_raw += packet.raw[2:]
|
||||||
|
packet_sent(packet)
|
||||||
Transport.transmit(outbound_interface, new_raw)
|
Transport.transmit(outbound_interface, new_raw)
|
||||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||||
sent = True
|
sent = True
|
||||||
@@ -722,6 +786,7 @@ class Transport:
|
|||||||
new_raw += packet.raw[1:2]
|
new_raw += packet.raw[1:2]
|
||||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||||
new_raw += packet.raw[2:]
|
new_raw += packet.raw[2:]
|
||||||
|
packet_sent(packet)
|
||||||
Transport.transmit(outbound_interface, new_raw)
|
Transport.transmit(outbound_interface, new_raw)
|
||||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||||
sent = True
|
sent = True
|
||||||
@@ -730,6 +795,7 @@ class Transport:
|
|||||||
# directly reachable, and also on which interface, so we
|
# directly reachable, and also on which interface, so we
|
||||||
# simply transmit the packet directly on that one.
|
# simply transmit the packet directly on that one.
|
||||||
else:
|
else:
|
||||||
|
packet_sent(packet)
|
||||||
Transport.transmit(outbound_interface, packet.raw)
|
Transport.transmit(outbound_interface, packet.raw)
|
||||||
sent = True
|
sent = True
|
||||||
|
|
||||||
@@ -813,7 +879,7 @@ class Transport:
|
|||||||
interface.announce_queue = []
|
interface.announce_queue = []
|
||||||
|
|
||||||
queued_announces = True if len(interface.announce_queue) > 0 else False
|
queued_announces = True if len(interface.announce_queue) > 0 else False
|
||||||
if not queued_announces and outbound_time > interface.announce_allowed_at:
|
if not queued_announces and outbound_time > interface.announce_allowed_at and interface.bitrate != None and interface.bitrate != 0:
|
||||||
tx_time = (len(packet.raw)*8) / interface.bitrate
|
tx_time = (len(packet.raw)*8) / interface.bitrate
|
||||||
wait_time = (tx_time / interface.announce_cap)
|
wait_time = (tx_time / interface.announce_cap)
|
||||||
interface.announce_allowed_at = outbound_time + wait_time
|
interface.announce_allowed_at = outbound_time + wait_time
|
||||||
@@ -896,28 +962,9 @@ class Transport:
|
|||||||
Transport.transmit(interface, packet.raw)
|
Transport.transmit(interface, packet.raw)
|
||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
interface.sent_announce()
|
interface.sent_announce()
|
||||||
|
packet_sent(packet)
|
||||||
sent = True
|
sent = True
|
||||||
|
|
||||||
if sent:
|
|
||||||
packet.sent = True
|
|
||||||
packet.sent_at = time.time()
|
|
||||||
|
|
||||||
# Don't generate receipt if it has been explicitly disabled
|
|
||||||
if (packet.create_receipt == True and
|
|
||||||
# Only generate receipts for DATA packets
|
|
||||||
packet.packet_type == RNS.Packet.DATA and
|
|
||||||
# Don't generate receipts for PLAIN destinations
|
|
||||||
packet.destination.type != RNS.Destination.PLAIN and
|
|
||||||
# Don't generate receipts for link-related packets
|
|
||||||
not (packet.context >= RNS.Packet.KEEPALIVE and packet.context <= RNS.Packet.LRPROOF) and
|
|
||||||
# Don't generate receipts for resource packets
|
|
||||||
not (packet.context >= RNS.Packet.RESOURCE and packet.context <= RNS.Packet.RESOURCE_RCL)):
|
|
||||||
|
|
||||||
packet.receipt = RNS.PacketReceipt(packet)
|
|
||||||
Transport.receipts.append(packet.receipt)
|
|
||||||
|
|
||||||
Transport.cache(packet)
|
|
||||||
|
|
||||||
Transport.jobs_locked = False
|
Transport.jobs_locked = False
|
||||||
return sent
|
return sent
|
||||||
|
|
||||||
@@ -926,6 +973,13 @@ class Transport:
|
|||||||
# TODO: Think long and hard about this.
|
# TODO: Think long and hard about this.
|
||||||
# Is it even strictly necessary with the current
|
# Is it even strictly necessary with the current
|
||||||
# transport rules?
|
# transport rules?
|
||||||
|
|
||||||
|
# Filter packets intended for other transport instances
|
||||||
|
if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE:
|
||||||
|
if packet.transport_id != Transport.identity.hash:
|
||||||
|
RNS.log("Ignored packet "+RNS.prettyhexrep(packet.packet_hash)+" in transport for other transport instance", RNS.LOG_EXTREME)
|
||||||
|
return False
|
||||||
|
|
||||||
if packet.context == RNS.Packet.KEEPALIVE:
|
if packet.context == RNS.Packet.KEEPALIVE:
|
||||||
return True
|
return True
|
||||||
if packet.context == RNS.Packet.RESOURCE_REQ:
|
if packet.context == RNS.Packet.RESOURCE_REQ:
|
||||||
@@ -1074,6 +1128,15 @@ class Transport:
|
|||||||
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(0)
|
Transport.local_client_snr_cache.pop(0)
|
||||||
|
|
||||||
|
if hasattr(interface, "r_stat_q"):
|
||||||
|
if interface.r_stat_q != None:
|
||||||
|
packet.q = interface.r_stat_q
|
||||||
|
if len(Transport.local_client_interfaces) > 0:
|
||||||
|
Transport.local_client_q_cache.append([packet.packet_hash, packet.q])
|
||||||
|
|
||||||
|
while len(Transport.local_client_q_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
||||||
|
Transport.local_client_q_cache.pop(0)
|
||||||
|
|
||||||
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):
|
||||||
packet.hops -= 1
|
packet.hops -= 1
|
||||||
@@ -1081,10 +1144,31 @@ class Transport:
|
|||||||
elif Transport.interface_to_shared_instance(interface):
|
elif Transport.interface_to_shared_instance(interface):
|
||||||
packet.hops -= 1
|
packet.hops -= 1
|
||||||
|
|
||||||
|
|
||||||
if Transport.packet_filter(packet):
|
if Transport.packet_filter(packet):
|
||||||
|
# By default, remember packet hashes to avoid routing
|
||||||
|
# loops in the network, using the packet filter.
|
||||||
|
remember_packet_hash = True
|
||||||
|
|
||||||
|
# If this packet belongs to a link in our link table,
|
||||||
|
# we'll have to defer adding it to the filter list.
|
||||||
|
# In some cases, we might see a packet over a shared-
|
||||||
|
# medium interface, belonging to a link that transports
|
||||||
|
# or terminates with this instance, but before it would
|
||||||
|
# normally reach us. If the packet is appended to the
|
||||||
|
# filter list at this point, link transport will break.
|
||||||
|
if packet.destination_hash in Transport.link_table:
|
||||||
|
remember_packet_hash = False
|
||||||
|
|
||||||
|
# If this is a link request proof, don't add it until
|
||||||
|
# we are sure it's not actually somewhere else in the
|
||||||
|
# routing chain.
|
||||||
|
if packet.packet_type == RNS.Packet.PROOF and packet.context == RNS.Packet.LRPROOF:
|
||||||
|
remember_packet_hash = False
|
||||||
|
|
||||||
|
if remember_packet_hash:
|
||||||
Transport.packet_hashlist.append(packet.packet_hash)
|
Transport.packet_hashlist.append(packet.packet_hash)
|
||||||
Transport.cache(packet)
|
# TODO: Enable when caching has been redesigned
|
||||||
|
# Transport.cache(packet)
|
||||||
|
|
||||||
# Check special conditions for local clients connected
|
# Check special conditions for local clients connected
|
||||||
# through a shared Reticulum instance
|
# through a shared Reticulum instance
|
||||||
@@ -1165,7 +1249,8 @@ class Transport:
|
|||||||
|
|
||||||
if packet.packet_type == RNS.Packet.LINKREQUEST:
|
if packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
proof_timeout = now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops)
|
proof_timeout = Transport.extra_link_proof_timeout(packet.receiving_interface)
|
||||||
|
proof_timeout += now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops)
|
||||||
|
|
||||||
# Entry format is
|
# Entry format is
|
||||||
link_entry = [ now, # 0: Timestamp,
|
link_entry = [ now, # 0: Timestamp,
|
||||||
@@ -1204,7 +1289,7 @@ class Transport:
|
|||||||
link_entry = Transport.link_table[packet.destination_hash]
|
link_entry = Transport.link_table[packet.destination_hash]
|
||||||
# If receiving and outbound interface is
|
# If receiving and outbound interface is
|
||||||
# the same for this link, direction doesn't
|
# the same for this link, direction doesn't
|
||||||
# matter, and we simply send the packet on.
|
# matter, and we simply repeat the packet.
|
||||||
outbound_interface = None
|
outbound_interface = None
|
||||||
if link_entry[2] == link_entry[4]:
|
if link_entry[2] == link_entry[4]:
|
||||||
# But check that taken hops matches one
|
# But check that taken hops matches one
|
||||||
@@ -1225,6 +1310,11 @@ class Transport:
|
|||||||
outbound_interface = link_entry[2]
|
outbound_interface = link_entry[2]
|
||||||
|
|
||||||
if outbound_interface != None:
|
if outbound_interface != None:
|
||||||
|
# Add this packet to the filter hashlist if we
|
||||||
|
# have determined that it's actually our turn
|
||||||
|
# to process it.
|
||||||
|
Transport.packet_hashlist.append(packet.packet_hash)
|
||||||
|
|
||||||
new_raw = packet.raw[0:1]
|
new_raw = packet.raw[0:1]
|
||||||
new_raw += struct.pack("!B", packet.hops)
|
new_raw += struct.pack("!B", packet.hops)
|
||||||
new_raw += packet.raw[2:]
|
new_raw += packet.raw[2:]
|
||||||
@@ -1267,6 +1357,7 @@ class Transport:
|
|||||||
announce_entry[6] += 1
|
announce_entry[6] += 1
|
||||||
if announce_entry[6] >= Transport.LOCAL_REBROADCASTS_MAX:
|
if announce_entry[6] >= Transport.LOCAL_REBROADCASTS_MAX:
|
||||||
RNS.log("Max local rebroadcasts of announce for "+RNS.prettyhexrep(packet.destination_hash)+" reached, dropping announce from our table", RNS.LOG_DEBUG)
|
RNS.log("Max local rebroadcasts of announce for "+RNS.prettyhexrep(packet.destination_hash)+" reached, dropping announce from our table", RNS.LOG_DEBUG)
|
||||||
|
if packet.destination_hash in Transport.announce_table:
|
||||||
Transport.announce_table.pop(packet.destination_hash)
|
Transport.announce_table.pop(packet.destination_hash)
|
||||||
|
|
||||||
if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0:
|
if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0:
|
||||||
@@ -1302,6 +1393,7 @@ class Transport:
|
|||||||
# TODO: Check whether this approach works
|
# TODO: Check whether this approach works
|
||||||
# under all circumstances
|
# under all circumstances
|
||||||
if not random_blob in random_blobs:
|
if not random_blob in random_blobs:
|
||||||
|
Transport.mark_path_unknown_state(packet.destination_hash)
|
||||||
should_add = True
|
should_add = True
|
||||||
else:
|
else:
|
||||||
should_add = False
|
should_add = False
|
||||||
@@ -1319,21 +1411,39 @@ class Transport:
|
|||||||
if path_announce_emitted >= announce_emitted:
|
if path_announce_emitted >= announce_emitted:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# If the path has expired, consider this
|
||||||
|
# announce for adding to the path table.
|
||||||
if (now >= path_expires):
|
if (now >= path_expires):
|
||||||
# We also check that the announce is
|
# We check that the announce is
|
||||||
# different from ones we've already heard,
|
# different from ones we've already heard,
|
||||||
# to avoid loops in the network
|
# to avoid loops in the network
|
||||||
if not random_blob in random_blobs:
|
if not random_blob in random_blobs:
|
||||||
# TODO: Check that this ^ approach actually
|
# TODO: Check that this ^ approach actually
|
||||||
# works under all circumstances
|
# works under all circumstances
|
||||||
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce due to expired path", RNS.LOG_DEBUG)
|
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce due to expired path", RNS.LOG_DEBUG)
|
||||||
|
Transport.mark_path_unknown_state(packet.destination_hash)
|
||||||
should_add = True
|
should_add = True
|
||||||
else:
|
else:
|
||||||
should_add = False
|
should_add = False
|
||||||
else:
|
else:
|
||||||
|
# If the path is not expired, but the emission
|
||||||
|
# is more recent, and we haven't already heard
|
||||||
|
# this announce before, update the path table.
|
||||||
if (announce_emitted > path_announce_emitted):
|
if (announce_emitted > path_announce_emitted):
|
||||||
if not random_blob in random_blobs:
|
if not random_blob in random_blobs:
|
||||||
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since it was more recently emitted", RNS.LOG_DEBUG)
|
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since it was more recently emitted", RNS.LOG_DEBUG)
|
||||||
|
Transport.mark_path_unknown_state(packet.destination_hash)
|
||||||
|
should_add = True
|
||||||
|
else:
|
||||||
|
should_add = False
|
||||||
|
|
||||||
|
# If we have already heard this announce before,
|
||||||
|
# but the path has been marked as unresponsive
|
||||||
|
# by a failed communications attempt or similar,
|
||||||
|
# allow updating the path table to this one.
|
||||||
|
elif announce_emitted == path_announce_emitted:
|
||||||
|
if Transport.path_is_unresponsive(packet.destination_hash):
|
||||||
|
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since previously tried path was unresponsive", RNS.LOG_DEBUG)
|
||||||
should_add = True
|
should_add = True
|
||||||
else:
|
else:
|
||||||
should_add = False
|
should_add = False
|
||||||
@@ -1397,6 +1507,7 @@ class Transport:
|
|||||||
expires = now + Transport.PATHFINDER_E
|
expires = now + Transport.PATHFINDER_E
|
||||||
|
|
||||||
random_blobs.append(random_blob)
|
random_blobs.append(random_blob)
|
||||||
|
random_blobs = random_blobs[-Transport.MAX_RANDOM_BLOBS:]
|
||||||
|
|
||||||
if (RNS.Reticulum.transport_enabled() or Transport.from_local_client(packet)) and packet.context != RNS.Packet.PATH_RESPONSE:
|
if (RNS.Reticulum.transport_enabled() or Transport.from_local_client(packet)) and packet.context != RNS.Packet.PATH_RESPONSE:
|
||||||
# Insert announce into announce table for retransmission
|
# Insert announce into announce table for retransmission
|
||||||
@@ -1601,6 +1712,7 @@ class Transport:
|
|||||||
# needs to be transported
|
# needs to be transported
|
||||||
if (RNS.Reticulum.transport_enabled() or for_local_client_link or from_local_client) and packet.destination_hash in Transport.link_table:
|
if (RNS.Reticulum.transport_enabled() or for_local_client_link or from_local_client) and packet.destination_hash in Transport.link_table:
|
||||||
link_entry = Transport.link_table[packet.destination_hash]
|
link_entry = Transport.link_table[packet.destination_hash]
|
||||||
|
if packet.hops == link_entry[3]:
|
||||||
if packet.receiving_interface == link_entry[2]:
|
if packet.receiving_interface == link_entry[2]:
|
||||||
try:
|
try:
|
||||||
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
|
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
|
||||||
@@ -1627,11 +1739,30 @@ class Transport:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||||
|
else:
|
||||||
|
RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG)
|
||||||
else:
|
else:
|
||||||
# Check if we can deliver it to a local
|
# Check if we can deliver it to a local
|
||||||
# pending link
|
# pending link
|
||||||
for link in Transport.pending_links:
|
for link in Transport.pending_links:
|
||||||
if link.link_id == packet.destination_hash:
|
if link.link_id == packet.destination_hash:
|
||||||
|
# We need to also allow an expected hops value of
|
||||||
|
# PATHFINDER_M, since in some cases, the number of hops
|
||||||
|
# to the destination will be unknown at link creation
|
||||||
|
# time. The real chance of this occuring is likely to be
|
||||||
|
# extremely small, and this allowance could probably
|
||||||
|
# be discarded without major issues, but it is kept
|
||||||
|
# for now to ensure backwards compatibility.
|
||||||
|
|
||||||
|
# TODO: Probably reset check back to
|
||||||
|
# if packet.hops == link.expected_hops:
|
||||||
|
# within one of the next releases
|
||||||
|
|
||||||
|
if packet.hops == link.expected_hops or link.expected_hops == RNS.Transport.PATHFINDER_M:
|
||||||
|
# Add this packet to the filter hashlist if we
|
||||||
|
# have determined that it's actually destined
|
||||||
|
# for this system, and then validate the proof
|
||||||
|
Transport.packet_hashlist.append(packet.packet_hash)
|
||||||
link.validate_proof(packet)
|
link.validate_proof(packet)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.RESOURCE_PRF:
|
elif packet.context == RNS.Packet.RESOURCE_PRF:
|
||||||
@@ -1649,7 +1780,7 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
proof_hash = None
|
proof_hash = None
|
||||||
|
|
||||||
# Check if this proof neds to be transported
|
# Check if this proof needs to be transported
|
||||||
if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table:
|
if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table:
|
||||||
reverse_entry = Transport.reverse_table.pop(packet.destination_hash)
|
reverse_entry = Transport.reverse_table.pop(packet.destination_hash)
|
||||||
if packet.receiving_interface == reverse_entry[1]:
|
if packet.receiving_interface == reverse_entry[1]:
|
||||||
@@ -1668,9 +1799,6 @@ class Transport:
|
|||||||
if receipt.hash == proof_hash:
|
if receipt.hash == proof_hash:
|
||||||
receipt_validated = receipt.validate_proof_packet(packet)
|
receipt_validated = receipt.validate_proof_packet(packet)
|
||||||
else:
|
else:
|
||||||
# TODO: This looks like it should actually
|
|
||||||
# be rewritten when implicit proofs are added.
|
|
||||||
|
|
||||||
# In case of an implicit proof, we have
|
# In case of an implicit proof, we have
|
||||||
# to check every single outstanding receipt
|
# to check every single outstanding receipt
|
||||||
receipt_validated = receipt.validate_proof_packet(packet)
|
receipt_validated = receipt.validate_proof_packet(packet)
|
||||||
@@ -1975,6 +2103,45 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def next_hop_interface_bitrate(destination_hash):
|
||||||
|
next_hop_interface = Transport.next_hop_interface(destination_hash)
|
||||||
|
if next_hop_interface != None:
|
||||||
|
return next_hop_interface.bitrate
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def next_hop_per_bit_latency(destination_hash):
|
||||||
|
next_hop_interface_bitrate = Transport.next_hop_interface_bitrate(destination_hash)
|
||||||
|
if next_hop_interface_bitrate != None:
|
||||||
|
return (1/next_hop_interface_bitrate)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def next_hop_per_byte_latency(destination_hash):
|
||||||
|
per_bit_latency = Transport.next_hop_per_bit_latency(destination_hash)
|
||||||
|
if per_bit_latency != None:
|
||||||
|
return per_bit_latency*8
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def first_hop_timeout(destination_hash):
|
||||||
|
latency = Transport.next_hop_per_byte_latency(destination_hash)
|
||||||
|
if latency != None:
|
||||||
|
return RNS.Reticulum.MTU * latency + RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||||
|
else:
|
||||||
|
return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extra_link_proof_timeout(interface):
|
||||||
|
if interface != None:
|
||||||
|
return ((1/interface.bitrate)*8)*RNS.Reticulum.MTU
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def expire_path(destination_hash):
|
def expire_path(destination_hash):
|
||||||
if destination_hash in Transport.destination_table:
|
if destination_hash in Transport.destination_table:
|
||||||
@@ -1984,6 +2151,38 @@ class Transport:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mark_path_unresponsive(destination_hash):
|
||||||
|
if destination_hash in Transport.destination_table:
|
||||||
|
Transport.path_states[destination_hash] = Transport.STATE_UNRESPONSIVE
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mark_path_responsive(destination_hash):
|
||||||
|
if destination_hash in Transport.destination_table:
|
||||||
|
Transport.path_states[destination_hash] = Transport.STATE_RESPONSIVE
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mark_path_unknown_state(destination_hash):
|
||||||
|
if destination_hash in Transport.destination_table:
|
||||||
|
Transport.path_states[destination_hash] = Transport.STATE_UNKNOWN
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def path_is_unresponsive(destination_hash):
|
||||||
|
if destination_hash in Transport.path_states:
|
||||||
|
if Transport.path_states[destination_hash] == Transport.STATE_UNRESPONSIVE:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def request_path(destination_hash, on_interface=None, tag=None, recursive=False):
|
def request_path(destination_hash, on_interface=None, tag=None, recursive=False):
|
||||||
"""
|
"""
|
||||||
@@ -2141,8 +2340,18 @@ class Transport:
|
|||||||
if is_from_local_client:
|
if is_from_local_client:
|
||||||
retransmit_timeout = now
|
retransmit_timeout = now
|
||||||
else:
|
else:
|
||||||
# TODO: Look at this timing
|
if Transport.is_local_client_interface(Transport.next_hop_interface(destination_hash)):
|
||||||
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
|
RNS.log("Path request destination "+RNS.prettyhexrep(destination_hash)+" is on a local client interface, rebroadcasting immediately", RNS.LOG_EXTREME)
|
||||||
|
retransmit_timeout = now
|
||||||
|
|
||||||
|
else:
|
||||||
|
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE
|
||||||
|
|
||||||
|
# If we are answering on a roaming-mode interface, wait a
|
||||||
|
# little longer, to allow potential more well-connected
|
||||||
|
# peers to answer first.
|
||||||
|
if attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||||
|
retransmit_timeout += Transport.PATH_REQUEST_RG
|
||||||
|
|
||||||
# This handles an edge case where a peer sends a past
|
# This handles an edge case where a peer sends a past
|
||||||
# request for a destination just after an announce for
|
# request for a destination just after an announce for
|
||||||
@@ -2238,7 +2447,10 @@ class Transport:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
for interface in detachable_interfaces:
|
for interface in detachable_interfaces:
|
||||||
|
try:
|
||||||
interface.detach()
|
interface.detach()
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("An error occurred while detaching "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def shared_connection_disappeared():
|
def shared_connection_disappeared():
|
||||||
@@ -2436,7 +2648,7 @@ class Transport:
|
|||||||
received_from = de[1]
|
received_from = de[1]
|
||||||
hops = de[2]
|
hops = de[2]
|
||||||
expires = de[3]
|
expires = de[3]
|
||||||
random_blobs = de[4]
|
random_blobs = de[4][-Transport.PERSIST_RANDOM_BLOBS:]
|
||||||
packet_hash = de[6].get_hash()
|
packet_hash = de[6].get_hash()
|
||||||
|
|
||||||
serialised_entry = [
|
serialised_entry = [
|
||||||
|
|||||||
+89
-8
@@ -64,19 +64,22 @@ 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("-i", "--identity", metavar="identity", action="store", default=None, help="hexadecimal Reticulum Destination hash or path to Identity file", type=str)
|
parser.add_argument("-i", "--identity", metavar="identity", action="store", default=None, help="hexadecimal Reticulum Destination hash or path to Identity file", type=str)
|
||||||
parser.add_argument("-g", "--generate", metavar="path", action="store", default=None, help="generate a new Identity")
|
parser.add_argument("-g", "--generate", metavar="file", action="store", default=None, help="generate a new Identity")
|
||||||
|
parser.add_argument("-m", "--import", dest="import_str", metavar="identity_data", action="store", default=None, help="import Reticulum identity in hex, base32 or base64 format", type=str)
|
||||||
|
parser.add_argument("-x", "--export", action="store_true", default=None, help="export identity to hex, base32 or base64 format")
|
||||||
|
|
||||||
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("-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 hashes 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="file", 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="file", 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")
|
||||||
parser.add_argument("-V", "--validate", metavar="path", action="store", default=None, help="validate signature")
|
parser.add_argument("-V", "--validate", metavar="path", action="store", default=None, help="validate signature")
|
||||||
|
|
||||||
parser.add_argument("-r", "--read", metavar="path", action="store", default=None, help="input file path", type=str)
|
parser.add_argument("-r", "--read", metavar="file", action="store", default=None, help="input file path", type=str)
|
||||||
parser.add_argument("-w", "--write", metavar="path", action="store", default=None, help="output file path", type=str)
|
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
|
||||||
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
||||||
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
|
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
|
||||||
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
|
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
|
||||||
@@ -86,7 +89,8 @@ def main():
|
|||||||
parser.add_argument("-p", "--print-identity", action="store_true", default=False, help="print identity info and exit")
|
parser.add_argument("-p", "--print-identity", action="store_true", default=False, help="print identity info and exit")
|
||||||
parser.add_argument("-P", "--print-private", action="store_true", default=False, help="allow displaying private keys")
|
parser.add_argument("-P", "--print-private", action="store_true", default=False, help="allow displaying private keys")
|
||||||
|
|
||||||
parser.add_argument("-b", "--base64", action="store_true", default=False, help=argparse.SUPPRESS) # help="Use base64-encoded input and output")
|
parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output")
|
||||||
|
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
|
||||||
|
|
||||||
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
|
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
|
||||||
|
|
||||||
@@ -110,6 +114,59 @@ def main():
|
|||||||
args.read = args.sign
|
args.read = args.sign
|
||||||
|
|
||||||
identity_str = args.identity
|
identity_str = args.identity
|
||||||
|
if args.import_str:
|
||||||
|
identity_bytes = None
|
||||||
|
try:
|
||||||
|
if args.base64:
|
||||||
|
identity_bytes = base64.urlsafe_b64decode(args.import_str)
|
||||||
|
elif args.base32:
|
||||||
|
identity_bytes = base64.b32decode(args.import_str)
|
||||||
|
else:
|
||||||
|
identity_bytes = bytes.fromhex(args.import_str)
|
||||||
|
except Exception as e:
|
||||||
|
print("Invalid identity data specified for import: "+str(e))
|
||||||
|
exit(41)
|
||||||
|
|
||||||
|
try:
|
||||||
|
identity = RNS.Identity.from_bytes(identity_bytes)
|
||||||
|
except Exception as e:
|
||||||
|
print("Could not create Reticulum identity from specified data: "+str(e))
|
||||||
|
exit(42)
|
||||||
|
|
||||||
|
RNS.log("Identity imported")
|
||||||
|
if args.base64:
|
||||||
|
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
|
||||||
|
if identity.prv:
|
||||||
|
if args.print_private:
|
||||||
|
if args.base64:
|
||||||
|
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
|
||||||
|
else:
|
||||||
|
RNS.log("Private Key : Hidden")
|
||||||
|
|
||||||
|
if args.write:
|
||||||
|
try:
|
||||||
|
wp = os.path.expanduser(args.write)
|
||||||
|
if not os.path.isfile(wp) or args.force:
|
||||||
|
identity.to_file(wp)
|
||||||
|
RNS.log("Wrote imported identity to "+str(args.write))
|
||||||
|
else:
|
||||||
|
print("File "+str(wp)+" already exists, not overwriting")
|
||||||
|
exit(43)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Error while writing imported identity to file: "+str(e))
|
||||||
|
exit(44)
|
||||||
|
|
||||||
|
exit(0)
|
||||||
|
|
||||||
if not args.generate and not identity_str:
|
if not args.generate and not identity_str:
|
||||||
print("\nNo identity provided, cannot continue\n")
|
print("\nNo identity provided, cannot continue\n")
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
@@ -246,14 +303,38 @@ def main():
|
|||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
if args.print_identity:
|
if args.print_identity:
|
||||||
RNS.log("Public Key : "+RNS.hexrep(identity.pub_bytes, delimit=False))
|
if args.base64:
|
||||||
|
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
|
||||||
if identity.prv:
|
if identity.prv:
|
||||||
if args.print_private:
|
if args.print_private:
|
||||||
RNS.log("Private Key : "+RNS.hexrep(identity.prv_bytes, delimit=False))
|
if args.base64:
|
||||||
|
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
|
||||||
else:
|
else:
|
||||||
RNS.log("Private Key : Hidden")
|
RNS.log("Private Key : Hidden")
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
if args.export:
|
||||||
|
if identity.prv:
|
||||||
|
if args.base64:
|
||||||
|
RNS.log("Exported Identity : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Exported Identity : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Exported Identity : "+RNS.hexrep(identity.get_private_key(), delimit=False))
|
||||||
|
else:
|
||||||
|
RNS.log("Identity doesn't hold a private key, cannot export")
|
||||||
|
exit(50)
|
||||||
|
|
||||||
|
exit(0)
|
||||||
|
|
||||||
if args.validate:
|
if args.validate:
|
||||||
if not args.read and args.validate.lower().endswith("."+SIG_EXT):
|
if not args.read and args.validate.lower().endswith("."+SIG_EXT):
|
||||||
args.read = str(args.validate).replace("."+SIG_EXT, "")
|
args.read = str(args.validate).replace("."+SIG_EXT, "")
|
||||||
|
|||||||
+347
-73
@@ -42,8 +42,8 @@ RNS.logtimefmt = "%H:%M:%S"
|
|||||||
RNS.compact_log_fmt = True
|
RNS.compact_log_fmt = True
|
||||||
|
|
||||||
program_version = "2.1.3"
|
program_version = "2.1.3"
|
||||||
eth_addr = "0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a"
|
eth_addr = "0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73"
|
||||||
btc_addr = "3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq"
|
btc_addr = "35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH"
|
||||||
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
|
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
|
||||||
|
|
||||||
rnode = None
|
rnode = None
|
||||||
@@ -53,6 +53,7 @@ rnode_baudrate = 115200
|
|||||||
known_keys = [["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100bf831ebd99f43b477caf1a094bec829389da40653e8f1f83fc14bf1b98a3e1cc70e759c213a43f71e5a47eb56a9ca487f241335b3e6ff7cdde0ee0a1c75c698574aeba0485726b6a9dfc046b4188e3520271ee8555a8f405cf21f81f2575771d0b0887adea5dd53c1f594f72c66b5f14904ffc2e72206a6698a490d51ba1105b0203010001"], ["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100e5d46084e445595376bf7efd9c6ccf19d39abbc59afdb763207e4ff68b8d00ebffb63847aa2fe6dd10783d3ea63b55ac66f71ad885c20e223709f0d51ed5c6c0d0b093be9e1d165bb8a483a548b67a3f7a1e4580f50e75b306593fa6067ae259d3e297717bd7ff8c8f5b07f2bed89929a9a0321026cf3699524db98e2d18fb2d020300ff39"]]
|
known_keys = [["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100bf831ebd99f43b477caf1a094bec829389da40653e8f1f83fc14bf1b98a3e1cc70e759c213a43f71e5a47eb56a9ca487f241335b3e6ff7cdde0ee0a1c75c698574aeba0485726b6a9dfc046b4188e3520271ee8555a8f405cf21f81f2575771d0b0887adea5dd53c1f594f72c66b5f14904ffc2e72206a6698a490d51ba1105b0203010001"], ["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100e5d46084e445595376bf7efd9c6ccf19d39abbc59afdb763207e4ff68b8d00ebffb63847aa2fe6dd10783d3ea63b55ac66f71ad885c20e223709f0d51ed5c6c0d0b093be9e1d165bb8a483a548b67a3f7a1e4580f50e75b306593fa6067ae259d3e297717bd7ff8c8f5b07f2bed89929a9a0321026cf3699524db98e2d18fb2d020300ff39"]]
|
||||||
firmware_update_url = "https://github.com/markqvist/RNode_Firmware/releases/download/"
|
firmware_update_url = "https://github.com/markqvist/RNode_Firmware/releases/download/"
|
||||||
fw_filename = None
|
fw_filename = None
|
||||||
|
fw_url = None
|
||||||
mapped_model = None
|
mapped_model = None
|
||||||
|
|
||||||
class KISS():
|
class KISS():
|
||||||
@@ -120,12 +121,16 @@ class KISS():
|
|||||||
class ROM():
|
class ROM():
|
||||||
PLATFORM_AVR = 0x90
|
PLATFORM_AVR = 0x90
|
||||||
PLATFORM_ESP32 = 0x80
|
PLATFORM_ESP32 = 0x80
|
||||||
|
PLATFORM_NRF52 = 0x70
|
||||||
|
|
||||||
MCU_1284P = 0x91
|
MCU_1284P = 0x91
|
||||||
MCU_2560 = 0x92
|
MCU_2560 = 0x92
|
||||||
MCU_ESP32 = 0x81
|
MCU_ESP32 = 0x81
|
||||||
|
MCU_NRF52 = 0x71
|
||||||
|
|
||||||
PRODUCT_RNODE = 0x03
|
PRODUCT_RNODE = 0x03
|
||||||
|
MODEL_A1 = 0xA1
|
||||||
|
MODEL_A6 = 0xA6
|
||||||
MODEL_A4 = 0xA4
|
MODEL_A4 = 0xA4
|
||||||
MODEL_A9 = 0xA9
|
MODEL_A9 = 0xA9
|
||||||
MODEL_A3 = 0xA3
|
MODEL_A3 = 0xA3
|
||||||
@@ -144,14 +149,23 @@ class ROM():
|
|||||||
PRODUCT_T32_21 = 0xB1
|
PRODUCT_T32_21 = 0xB1
|
||||||
MODEL_B4 = 0xB4
|
MODEL_B4 = 0xB4
|
||||||
MODEL_B9 = 0xB9
|
MODEL_B9 = 0xB9
|
||||||
|
MODEL_B4_TCXO = 0x04 # The TCXO model codes are only used here to select the
|
||||||
|
MODEL_B9_TCXO = 0x09 # correct firmware, actual model codes in firmware is
|
||||||
|
# still 0xB4 and 0xB9.
|
||||||
|
|
||||||
PRODUCT_H32_V2 = 0xC0
|
PRODUCT_H32_V2 = 0xC0
|
||||||
MODEL_C4 = 0xC4
|
MODEL_C4 = 0xC4
|
||||||
MODEL_C9 = 0xC9
|
MODEL_C9 = 0xC9
|
||||||
|
|
||||||
|
PRODUCT_H32_V3 = 0xC1
|
||||||
|
MODEL_C5 = 0xC5
|
||||||
|
MODEL_CA = 0xCA
|
||||||
|
|
||||||
PRODUCT_TBEAM = 0xE0
|
PRODUCT_TBEAM = 0xE0
|
||||||
MODEL_E4 = 0xE4
|
MODEL_E4 = 0xE4
|
||||||
MODEL_E9 = 0xE9
|
MODEL_E9 = 0xE9
|
||||||
|
MODEL_E3 = 0xE3
|
||||||
|
MODEL_E8 = 0xE8
|
||||||
|
|
||||||
PRODUCT_HMBRW = 0xF0
|
PRODUCT_HMBRW = 0xF0
|
||||||
MODEL_FF = 0xFF
|
MODEL_FF = 0xFF
|
||||||
@@ -182,6 +196,7 @@ class ROM():
|
|||||||
BOARD_GENERIC_ESP32 = 0x35
|
BOARD_GENERIC_ESP32 = 0x35
|
||||||
BOARD_LORA32_V2_0 = 0x36
|
BOARD_LORA32_V2_0 = 0x36
|
||||||
BOARD_LORA32_V2_1 = 0x37
|
BOARD_LORA32_V2_1 = 0x37
|
||||||
|
BOARD_RAK4630 = 0x51
|
||||||
|
|
||||||
mapped_product = ROM.PRODUCT_RNODE
|
mapped_product = ROM.PRODUCT_RNODE
|
||||||
products = {
|
products = {
|
||||||
@@ -192,38 +207,49 @@ products = {
|
|||||||
ROM.PRODUCT_T32_20: "LilyGO LoRa32 v2.0",
|
ROM.PRODUCT_T32_20: "LilyGO LoRa32 v2.0",
|
||||||
ROM.PRODUCT_T32_21: "LilyGO LoRa32 v2.1",
|
ROM.PRODUCT_T32_21: "LilyGO LoRa32 v2.1",
|
||||||
ROM.PRODUCT_H32_V2: "Heltec LoRa32 v2",
|
ROM.PRODUCT_H32_V2: "Heltec LoRa32 v2",
|
||||||
|
ROM.PRODUCT_H32_V3: "Heltec LoRa32 v3",
|
||||||
}
|
}
|
||||||
|
|
||||||
platforms = {
|
platforms = {
|
||||||
ROM.PLATFORM_AVR: "AVR",
|
ROM.PLATFORM_AVR: "AVR",
|
||||||
ROM.PLATFORM_ESP32:"ESP32",
|
ROM.PLATFORM_ESP32:"ESP32",
|
||||||
|
ROM.PLATFORM_NRF52:"NRF52",
|
||||||
}
|
}
|
||||||
|
|
||||||
mcus = {
|
mcus = {
|
||||||
ROM.MCU_1284P: "ATmega1284P",
|
ROM.MCU_1284P: "ATmega1284P",
|
||||||
ROM.MCU_2560:"ATmega2560",
|
ROM.MCU_2560:"ATmega2560",
|
||||||
ROM.MCU_ESP32:"Espressif Systems ESP32",
|
ROM.MCU_ESP32:"Espressif Systems ESP32",
|
||||||
|
ROM.MCU_NRF52:"Nordic nRF52840",
|
||||||
}
|
}
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex"],
|
0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex", "SX1278"],
|
||||||
0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex"],
|
0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex", "SX1276"],
|
||||||
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip"],
|
0xA1: [410000000, 525000000, 22, "410 - 525 MHz", "rnode_firmware_t3s3.zip", "SX1268"],
|
||||||
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip"],
|
0xA6: [820000000, 1020000000, 22, "820 - 960 MHz", "rnode_firmware_t3s3.zip", "SX1262"],
|
||||||
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip"],
|
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip", "SX1278"],
|
||||||
0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip"],
|
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip", "SX1276"],
|
||||||
0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip"],
|
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip", "SX1278"],
|
||||||
0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip"],
|
0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip", "SX1276"],
|
||||||
0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip"],
|
0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip", "SX1278"],
|
||||||
0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip"],
|
0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip", "SX1276"],
|
||||||
0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip"],
|
0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip", "SX1278"],
|
||||||
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip"],
|
0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip", "SX1276"],
|
||||||
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip"],
|
0x04: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1278"],
|
||||||
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip"],
|
0x09: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1276"],
|
||||||
0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip"],
|
0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip", "SX1278"],
|
||||||
0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip"],
|
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip", "SX1276"],
|
||||||
0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None],
|
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip", "SX1278"],
|
||||||
0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None],
|
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip", "SX1276"],
|
||||||
|
0xC5: [470000000, 510000000, 21, "470 - 510 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
|
||||||
|
0xCA: [863000000, 928000000, 21, "863 - 928 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
|
||||||
|
0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip", "SX1278"],
|
||||||
|
0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip", "SX1276"],
|
||||||
|
0xE3: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1268"],
|
||||||
|
0xE8: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1262"],
|
||||||
|
0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None, "Unknown"],
|
||||||
|
0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None, "Unknown"],
|
||||||
}
|
}
|
||||||
|
|
||||||
CNF_DIR = None
|
CNF_DIR = None
|
||||||
@@ -516,7 +542,7 @@ class RNode():
|
|||||||
if (len(command_buffer) == 4):
|
if (len(command_buffer) == 4):
|
||||||
self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
|
self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
|
||||||
elif (command == KISS.CMD_STAT_RSSI):
|
elif (command == KISS.CMD_STAT_RSSI):
|
||||||
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
self.r_stat_rssi = byte-157 # 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_RANDOM):
|
elif (command == KISS.CMD_RANDOM):
|
||||||
@@ -755,18 +781,13 @@ class RNode():
|
|||||||
self.made = bytes([self.eeprom[ROM.ADDR_MADE], self.eeprom[ROM.ADDR_MADE+1], self.eeprom[ROM.ADDR_MADE+2], self.eeprom[ROM.ADDR_MADE+3]])
|
self.made = bytes([self.eeprom[ROM.ADDR_MADE], self.eeprom[ROM.ADDR_MADE+1], self.eeprom[ROM.ADDR_MADE+2], self.eeprom[ROM.ADDR_MADE+3]])
|
||||||
self.checksum = b""
|
self.checksum = b""
|
||||||
|
|
||||||
|
|
||||||
self.min_freq = models[self.model][0]
|
|
||||||
self.max_freq = models[self.model][1]
|
|
||||||
self.max_output = models[self.model][2]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.min_freq = models[self.model][0]
|
self.min_freq = models[self.model][0]
|
||||||
self.max_freq = models[self.model][1]
|
self.max_freq = models[self.model][1]
|
||||||
self.max_output = models[self.model][2]
|
self.max_output = models[self.model][2]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Exception")
|
RNS.log("Error: Model band and output power capabilities are unknown!")
|
||||||
RNS.log(str(e))
|
RNS.log("The contained exception was: "+str(e))
|
||||||
self.min_freq = 0
|
self.min_freq = 0
|
||||||
self.max_freq = 0
|
self.max_freq = 0
|
||||||
self.max_output = 0
|
self.max_output = 0
|
||||||
@@ -928,6 +949,7 @@ class RNode():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.provisioned = False
|
self.provisioned = False
|
||||||
RNS.log("Invalid EEPROM data, could not parse device EEPROM.")
|
RNS.log("Invalid EEPROM data, could not parse device EEPROM.")
|
||||||
|
RNS.log("The contained exception was: "+str(e))
|
||||||
|
|
||||||
|
|
||||||
def device_probe(self):
|
def device_probe(self):
|
||||||
@@ -980,15 +1002,49 @@ def ensure_firmware_file(fw_filename):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if selected_version == None:
|
|
||||||
if not upd_nocheck:
|
if not upd_nocheck:
|
||||||
try:
|
try:
|
||||||
try:
|
# if custom firmware url, download latest release
|
||||||
|
if selected_version == None and fw_url == None:
|
||||||
|
version_url = firmware_version_url+fw_filename
|
||||||
|
RNS.log("Retrieving latest version info from "+version_url)
|
||||||
urlretrieve(firmware_version_url+fw_filename, UPD_DIR+"/"+fw_filename+".version.latest")
|
urlretrieve(firmware_version_url+fw_filename, UPD_DIR+"/"+fw_filename+".version.latest")
|
||||||
|
else:
|
||||||
|
if fw_url != None:
|
||||||
|
if selected_version == None:
|
||||||
|
version_url = fw_url+"latest/download/release.json"
|
||||||
|
else:
|
||||||
|
version_url = fw_url+"download/"+selected_version+"/release.json"
|
||||||
|
else:
|
||||||
|
version_url = firmware_update_url+selected_version+"/release.json"
|
||||||
|
try:
|
||||||
|
RNS.log("Retrieving specified version info from "+version_url)
|
||||||
|
urlretrieve(version_url, UPD_DIR+"/version_release_info.json")
|
||||||
|
import json
|
||||||
|
with open(UPD_DIR+"/version_release_info.json", "rb") as rif:
|
||||||
|
rdat = json.loads(rif.read())
|
||||||
|
variant = rdat[fw_filename]
|
||||||
|
with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
|
||||||
|
inf_str = str(variant["version"])+" "+str(variant["hash"])
|
||||||
|
verf.write(inf_str.encode("utf-8"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
RNS.log("Failed to retrive version information for your board.")
|
||||||
|
RNS.log("Check your internet connection and try again.")
|
||||||
|
RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
|
||||||
|
RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
|
||||||
|
exit()
|
||||||
|
except Exception as e:
|
||||||
|
# if custom firmware url, don't fallback
|
||||||
|
if fw_url != None:
|
||||||
|
RNS.log("Failed to retrive version information for your board from the specified url.")
|
||||||
|
RNS.log("Check your internet connection and try again.")
|
||||||
|
RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
|
||||||
|
RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
|
||||||
|
exit()
|
||||||
|
|
||||||
RNS.log("")
|
RNS.log("")
|
||||||
RNS.log("WARNING!")
|
RNS.log("WARNING!")
|
||||||
RNS.log("Failed to retrieve latest version information for your board from the default server")
|
RNS.log("Failed to retrieve latest version information for your board from the default server.")
|
||||||
RNS.log("Will retry using the following fallback URL: "+fallback_firmware_version_url)
|
RNS.log("Will retry using the following fallback URL: "+fallback_firmware_version_url)
|
||||||
RNS.log("")
|
RNS.log("")
|
||||||
RNS.log("Hit enter if you want to proceed")
|
RNS.log("Hit enter if you want to proceed")
|
||||||
@@ -1007,28 +1063,30 @@ def ensure_firmware_file(fw_filename):
|
|||||||
RNS.log("Error while trying fallback URL: "+str(e))
|
RNS.log("Error while trying fallback URL: "+str(e))
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
RNS.log("Failed to retrive latest version information for your board.")
|
|
||||||
RNS.log("Check your internet connection and try again.")
|
|
||||||
RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
|
|
||||||
RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
|
|
||||||
exit()
|
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb")
|
file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb")
|
||||||
release_info = file.read().decode("utf-8").strip()
|
release_info = file.read().decode("utf-8").strip()
|
||||||
selected_version = release_info.split()[0]
|
selected_version = release_info.split()[0]
|
||||||
|
if selected_version == "not":
|
||||||
|
RNS.log("No valid version found for this board, exiting.")
|
||||||
|
exit(199)
|
||||||
|
|
||||||
selected_hash = release_info.split()[1]
|
selected_hash = release_info.split()[1]
|
||||||
if not os.path.isdir(UPD_DIR+"/"+selected_version):
|
if not os.path.isdir(UPD_DIR+"/"+selected_version):
|
||||||
os.makedirs(UPD_DIR+"/"+selected_version)
|
os.makedirs(UPD_DIR+"/"+selected_version)
|
||||||
shutil.copy(UPD_DIR+"/"+fw_filename+".version.latest", UPD_DIR+"/"+selected_version+"/"+fw_filename+".version")
|
shutil.copy(UPD_DIR+"/"+fw_filename+".version.latest", UPD_DIR+"/"+selected_version+"/"+fw_filename+".version")
|
||||||
RNS.log("The latest firmware for this board is version "+selected_version)
|
RNS.log("The selected firmware for this board is version "+selected_version)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
|
RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
|
||||||
RNS.log("use the --fw-version option to manually specify a version.")
|
RNS.log("use the --fw-version option to manually specify a version.")
|
||||||
exit(98)
|
exit(98)
|
||||||
|
|
||||||
|
# if custom firmware url, use it
|
||||||
|
if fw_url != None:
|
||||||
|
update_target_url = fw_url+"download/"+selected_version+"/"+fw_filename
|
||||||
|
RNS.log("Retrieving firmware from custom url "+update_target_url)
|
||||||
|
else:
|
||||||
update_target_url = firmware_update_url+selected_version+"/"+fw_filename
|
update_target_url = firmware_update_url+selected_version+"/"+fw_filename
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1106,7 +1164,7 @@ device_signer = None
|
|||||||
force_update = False
|
force_update = False
|
||||||
upd_nocheck = False
|
upd_nocheck = False
|
||||||
def main():
|
def main():
|
||||||
global mapped_product, mapped_model, fw_filename, selected_version, force_update, upd_nocheck, device_signer
|
global mapped_product, mapped_model, fw_filename, fw_url, selected_version, force_update, upd_nocheck, device_signer
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not util.find_spec("serial"):
|
if not util.find_spec("serial"):
|
||||||
@@ -1138,6 +1196,7 @@ def main():
|
|||||||
parser.add_argument("-u", "--update", action="store_true", help="Update firmware to the latest version")
|
parser.add_argument("-u", "--update", action="store_true", help="Update firmware to the latest version")
|
||||||
parser.add_argument("-U", "--force-update", action="store_true", help="Update to specified firmware even if version matches or is older than installed version")
|
parser.add_argument("-U", "--force-update", action="store_true", help="Update to specified firmware even if version matches or is older than installed version")
|
||||||
parser.add_argument("--fw-version", action="store", metavar="version", default=None, help="Use a specific firmware version for update or autoinstall")
|
parser.add_argument("--fw-version", action="store", metavar="version", default=None, help="Use a specific firmware version for update or autoinstall")
|
||||||
|
parser.add_argument("--fw-url", action="store", metavar="url", default=None, help="Use an alternate firmware download URL")
|
||||||
parser.add_argument("--nocheck", action="store_true", help="Don't check for firmware updates online")
|
parser.add_argument("--nocheck", action="store_true", help="Don't check for firmware updates online")
|
||||||
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")
|
||||||
@@ -1169,15 +1228,15 @@ def main():
|
|||||||
|
|
||||||
parser.add_argument("--version", action="store_true", help="Print program version and exit")
|
parser.add_argument("--version", action="store_true", help="Print program version and exit")
|
||||||
|
|
||||||
parser.add_argument("-f", "--flash", action="store_true", help=argparse.SUPPRESS) # Flash firmware and bootstrap EEPROM
|
parser.add_argument("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM")
|
||||||
parser.add_argument("-r", "--rom", action="store_true", help=argparse.SUPPRESS) # Bootstrap EEPROM without flashing firmware
|
parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
|
||||||
parser.add_argument("-k", "--key", action="store_true", help=argparse.SUPPRESS) # Generate a new signing key and exit
|
parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") #
|
||||||
parser.add_argument("-S", "--sign", action="store_true", help=argparse.SUPPRESS) # Display public part of signing key
|
parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
|
||||||
parser.add_argument("-H", "--firmware-hash", action="store", help=argparse.SUPPRESS) # Display public part of signing key
|
parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash")
|
||||||
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help=argparse.SUPPRESS) # Platform specification for device bootstrap
|
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap")
|
||||||
parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help=argparse.SUPPRESS) # Product specification for device bootstrap
|
parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help="Product specification for device bootstrap") #
|
||||||
parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help=argparse.SUPPRESS) # Model code for device bootstrap
|
parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for device bootstrap")
|
||||||
parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help=argparse.SUPPRESS) # Hardware revision for device bootstrap
|
parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision for device bootstrap")
|
||||||
|
|
||||||
parser.add_argument("port", nargs="?", default=None, help="serial port where RNode is attached", type=str)
|
parser.add_argument("port", nargs="?", default=None, help="serial port where RNode is attached", type=str)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -1210,6 +1269,9 @@ def main():
|
|||||||
RNS.log("Selected version \""+selected_version+"\" does not appear to be a number.")
|
RNS.log("Selected version \""+selected_version+"\" does not appear to be a number.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
if args.fw_url != None:
|
||||||
|
fw_url = args.fw_url
|
||||||
|
|
||||||
if args.force_update:
|
if args.force_update:
|
||||||
force_update = True
|
force_update = True
|
||||||
|
|
||||||
@@ -1497,6 +1559,8 @@ def main():
|
|||||||
print("[5] LilyGO LoRa32 v1.0")
|
print("[5] LilyGO LoRa32 v1.0")
|
||||||
print("[6] LilyGO T-Beam")
|
print("[6] LilyGO T-Beam")
|
||||||
print("[7] Heltec LoRa32 v2")
|
print("[7] Heltec LoRa32 v2")
|
||||||
|
print("[8] Heltec LoRa32 v3")
|
||||||
|
#print("[9] LilyGO LoRa T3S3")
|
||||||
print(" .")
|
print(" .")
|
||||||
print(" / \\ Select one of these options if you want to easily turn")
|
print(" / \\ Select one of these options if you want to easily turn")
|
||||||
print(" | a supported development board into an RNode.")
|
print(" | a supported development board into an RNode.")
|
||||||
@@ -1507,7 +1571,8 @@ 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 > 7:
|
c_mod = False
|
||||||
|
if c_dev < 1 or c_dev > 9:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
elif c_dev == 1:
|
elif c_dev == 1:
|
||||||
selected_product = ROM.PRODUCT_RNODE
|
selected_product = ROM.PRODUCT_RNODE
|
||||||
@@ -1536,8 +1601,7 @@ def main():
|
|||||||
print(" T-Beam RNode Installer")
|
print(" T-Beam RNode Installer")
|
||||||
print("")
|
print("")
|
||||||
print("The RNode firmware can currently be installed on T-Beam devices using the")
|
print("The RNode firmware can currently be installed on T-Beam devices using the")
|
||||||
print("SX1276 and SX1278 transceiver chips. Support for devices with the newer")
|
print("SX1276, SX1278, SX1262 and SX1268 transceiver chips.")
|
||||||
print("SX1262 and SX1268 chips is in development.")
|
|
||||||
print("")
|
print("")
|
||||||
print("Important! Using RNode firmware on T-Beam devices should currently be")
|
print("Important! Using RNode firmware on T-Beam devices should currently be")
|
||||||
print("considered experimental. It is not intended for production or critical use.")
|
print("considered experimental. It is not intended for production or critical use.")
|
||||||
@@ -1608,6 +1672,39 @@ def main():
|
|||||||
print("who would like to experiment with it. Hit enter to continue.")
|
print("who would like to experiment with it. Hit enter to continue.")
|
||||||
print("---------------------------------------------------------------------------")
|
print("---------------------------------------------------------------------------")
|
||||||
input()
|
input()
|
||||||
|
elif c_dev == 9:
|
||||||
|
selected_product = ROM.PRODUCT_RNODE
|
||||||
|
c_mod = True
|
||||||
|
clear()
|
||||||
|
print("")
|
||||||
|
print("---------------------------------------------------------------------------")
|
||||||
|
print(" LilyGO LoRa32 T3S3 RNode Installer")
|
||||||
|
print("")
|
||||||
|
print("Important! Using RNode firmware on T3S3 devices should currently be")
|
||||||
|
print("considered experimental. It is not intended for production or critical use.")
|
||||||
|
print("")
|
||||||
|
print("Please note that Bluetooth is currently not implemented on this board.")
|
||||||
|
print("")
|
||||||
|
print("The currently supplied firmware is provided AS-IS as a courtesey to those")
|
||||||
|
print("who would like to experiment with it. Hit enter to continue.")
|
||||||
|
print("---------------------------------------------------------------------------")
|
||||||
|
input()
|
||||||
|
elif c_dev == 8:
|
||||||
|
selected_product = ROM.PRODUCT_H32_V3
|
||||||
|
clear()
|
||||||
|
print("")
|
||||||
|
print("---------------------------------------------------------------------------")
|
||||||
|
print(" Heltec LoRa32 v3.0 RNode Installer")
|
||||||
|
print("")
|
||||||
|
print("Important! Using RNode firmware on Heltec devices should currently be")
|
||||||
|
print("considered experimental. It is not intended for production or critical use.")
|
||||||
|
print("")
|
||||||
|
print("Please note that Bluetooth is currently not implemented on this board.")
|
||||||
|
print("")
|
||||||
|
print("The currently supplied firmware is provided AS-IS as a courtesey to those")
|
||||||
|
print("who would like to experiment with it. Hit enter to continue.")
|
||||||
|
print("---------------------------------------------------------------------------")
|
||||||
|
input()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("That device type does not exist, exiting now.")
|
print("That device type does not exist, exiting now.")
|
||||||
exit()
|
exit()
|
||||||
@@ -1660,26 +1757,22 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
elif selected_product == ROM.PRODUCT_RNODE:
|
elif selected_product == ROM.PRODUCT_RNODE:
|
||||||
|
if not c_mod:
|
||||||
selected_mcu = ROM.MCU_1284P
|
selected_mcu = ROM.MCU_1284P
|
||||||
print("\nWhat model is this RNode?\n")
|
print("\nWhat model is this RNode?\n")
|
||||||
print("[1] Handheld v2.x RNode, 410 - 525 MHz")
|
print("[1] Handheld v2.1 RNode, 410 - 525 MHz")
|
||||||
print("[2] Handheld v2.x RNode, 820 - 1020 MHz")
|
print("[2] Handheld v2.1 RNode, 820 - 1020 MHz")
|
||||||
print("")
|
print("")
|
||||||
print("[3] Original v1.x RNode, 410 - 525 MHz")
|
print("[3] Original v1.x RNode, 410 - 525 MHz")
|
||||||
print("[4] Original v1.x RNode, 820 - 1020 MHz")
|
print("[4] Original v1.x RNode, 820 - 1020 MHz")
|
||||||
# print("[5] Prototype v2 RNode, 410 - 525 MHz")
|
print("")
|
||||||
# print("[6] Prototype v2 RNode, 820 - 1020 MHz")
|
print("[5] Prototype v2.2 RNode, 410 - 525 MHz")
|
||||||
|
print("[6] Prototype v2.2 RNode, 820 - 1020 MHz")
|
||||||
print("\n? ", end="")
|
print("\n? ", end="")
|
||||||
try:
|
try:
|
||||||
c_model = int(input())
|
c_model = int(input())
|
||||||
if c_model < 1 or c_model > 6:
|
if c_model < 1 or c_model > 6:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
elif c_model == 3:
|
|
||||||
selected_model = ROM.MODEL_A4
|
|
||||||
selected_platform = ROM.PLATFORM_AVR
|
|
||||||
elif c_model == 4:
|
|
||||||
selected_model = ROM.MODEL_A9
|
|
||||||
selected_platform = ROM.PLATFORM_AVR
|
|
||||||
elif c_model == 1:
|
elif c_model == 1:
|
||||||
selected_model = ROM.MODEL_A2
|
selected_model = ROM.MODEL_A2
|
||||||
selected_mcu = ROM.MCU_ESP32
|
selected_mcu = ROM.MCU_ESP32
|
||||||
@@ -1688,6 +1781,20 @@ def main():
|
|||||||
selected_model = ROM.MODEL_A7
|
selected_model = ROM.MODEL_A7
|
||||||
selected_mcu = ROM.MCU_ESP32
|
selected_mcu = ROM.MCU_ESP32
|
||||||
selected_platform = ROM.PLATFORM_ESP32
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 3:
|
||||||
|
selected_model = ROM.MODEL_A4
|
||||||
|
selected_platform = ROM.PLATFORM_AVR
|
||||||
|
elif c_model == 4:
|
||||||
|
selected_model = ROM.MODEL_A9
|
||||||
|
selected_platform = ROM.PLATFORM_AVR
|
||||||
|
elif c_model == 5:
|
||||||
|
selected_model = ROM.MODEL_A1
|
||||||
|
selected_mcu = ROM.MCU_ESP32
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 6:
|
||||||
|
selected_model = ROM.MODEL_A6
|
||||||
|
selected_mcu = ROM.MCU_ESP32
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
# elif c_model == 5:
|
# elif c_model == 5:
|
||||||
# selected_model = ROM.MODEL_A3
|
# selected_model = ROM.MODEL_A3
|
||||||
# selected_mcu = ROM.MCU_ESP32
|
# selected_mcu = ROM.MCU_ESP32
|
||||||
@@ -1699,14 +1806,35 @@ def main():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("That model does not exist, exiting now.")
|
print("That model does not exist, exiting now.")
|
||||||
exit()
|
exit()
|
||||||
|
else:
|
||||||
|
print("\nWhat model is this T3S3?\n")
|
||||||
|
print("[1] 410 - 525 MHz (with SX1268 chip)")
|
||||||
|
print("[2] 820 - 1020 MHz (with SX1262 chip)")
|
||||||
|
print("\n? ", end="")
|
||||||
|
try:
|
||||||
|
c_model = int(input())
|
||||||
|
if c_model < 1 or c_model > 2:
|
||||||
|
raise ValueError()
|
||||||
|
elif c_model == 1:
|
||||||
|
selected_model = ROM.MODEL_A1
|
||||||
|
selected_mcu = ROM.MCU_ESP32
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 2:
|
||||||
|
selected_model = ROM.MODEL_A6
|
||||||
|
selected_mcu = ROM.MCU_ESP32
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
except Exception as e:
|
||||||
|
print("That model does not exist, exiting now.")
|
||||||
|
exit()
|
||||||
|
|
||||||
elif selected_product == ROM.PRODUCT_TBEAM:
|
elif selected_product == ROM.PRODUCT_TBEAM:
|
||||||
selected_mcu = ROM.MCU_ESP32
|
selected_mcu = ROM.MCU_ESP32
|
||||||
print("\nWhat band is this T-Beam for?\n")
|
print("\nWhat band is this T-Beam for?\n")
|
||||||
print("[1] 433 MHz")
|
print("[1] 433 MHz (with SX1278 chip)")
|
||||||
print("[2] 868 MHz")
|
print("[2] 868/915/923 MHz (with SX1276 chip)")
|
||||||
print("[3] 915 MHz")
|
print("");
|
||||||
print("[4] 923 MHz")
|
print("[3] 433 MHz (with SX1268 chip)")
|
||||||
|
print("[4] 868/915/923 MHz (with SX1262 chip)")
|
||||||
print("\n? ", end="")
|
print("\n? ", end="")
|
||||||
try:
|
try:
|
||||||
c_model = int(input())
|
c_model = int(input())
|
||||||
@@ -1715,9 +1843,15 @@ def main():
|
|||||||
elif c_model == 1:
|
elif c_model == 1:
|
||||||
selected_model = ROM.MODEL_E4
|
selected_model = ROM.MODEL_E4
|
||||||
selected_platform = ROM.PLATFORM_ESP32
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
elif c_model > 1:
|
elif c_model == 2:
|
||||||
selected_model = ROM.MODEL_E9
|
selected_model = ROM.MODEL_E9
|
||||||
selected_platform = ROM.PLATFORM_ESP32
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 3:
|
||||||
|
selected_model = ROM.MODEL_E3
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 4:
|
||||||
|
selected_model = ROM.MODEL_E8
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("That band does not exist, exiting now.")
|
print("That band does not exist, exiting now.")
|
||||||
exit()
|
exit()
|
||||||
@@ -1770,9 +1904,9 @@ def main():
|
|||||||
selected_mcu = ROM.MCU_ESP32
|
selected_mcu = ROM.MCU_ESP32
|
||||||
print("\nWhat band is this LoRa32 for?\n")
|
print("\nWhat band is this LoRa32 for?\n")
|
||||||
print("[1] 433 MHz")
|
print("[1] 433 MHz")
|
||||||
print("[2] 868 MHz")
|
print("[2] 868/915/923 MHz")
|
||||||
print("[3] 915 MHz")
|
print("[3] 433 MHz, with TCXO")
|
||||||
print("[4] 923 MHz")
|
print("[4] 868/915/923 MHz, with TCXO")
|
||||||
print("\n? ", end="")
|
print("\n? ", end="")
|
||||||
try:
|
try:
|
||||||
c_model = int(input())
|
c_model = int(input())
|
||||||
@@ -1781,9 +1915,15 @@ def main():
|
|||||||
elif c_model == 1:
|
elif c_model == 1:
|
||||||
selected_model = ROM.MODEL_B4
|
selected_model = ROM.MODEL_B4
|
||||||
selected_platform = ROM.PLATFORM_ESP32
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
elif c_model > 1:
|
elif c_model == 2:
|
||||||
selected_model = ROM.MODEL_B9
|
selected_model = ROM.MODEL_B9
|
||||||
selected_platform = ROM.PLATFORM_ESP32
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 3:
|
||||||
|
selected_model = ROM.MODEL_B4_TCXO
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model == 4:
|
||||||
|
selected_model = ROM.MODEL_B9_TCXO
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("That band does not exist, exiting now.")
|
print("That band does not exist, exiting now.")
|
||||||
exit()
|
exit()
|
||||||
@@ -1810,6 +1950,28 @@ def main():
|
|||||||
print("That band does not exist, exiting now.")
|
print("That band does not exist, exiting now.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
elif selected_product == ROM.PRODUCT_H32_V3:
|
||||||
|
selected_mcu = ROM.MCU_ESP32
|
||||||
|
print("\nWhat band is this Heltec LoRa32 V3 for?\n")
|
||||||
|
print("[1] 433 MHz")
|
||||||
|
print("[2] 868 MHz")
|
||||||
|
print("[3] 915 MHz")
|
||||||
|
print("[4] 923 MHz")
|
||||||
|
print("\n? ", end="")
|
||||||
|
try:
|
||||||
|
c_model = int(input())
|
||||||
|
if c_model < 1 or c_model > 4:
|
||||||
|
raise ValueError()
|
||||||
|
elif c_model == 1:
|
||||||
|
selected_model = ROM.MODEL_C5
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
elif c_model > 1:
|
||||||
|
selected_model = ROM.MODEL_CA
|
||||||
|
selected_platform = ROM.PLATFORM_ESP32
|
||||||
|
except Exception as e:
|
||||||
|
print("That band does not exist, exiting now.")
|
||||||
|
exit()
|
||||||
|
|
||||||
if selected_model != ROM.MODEL_FF and selected_model != ROM.MODEL_FE:
|
if selected_model != ROM.MODEL_FF and selected_model != ROM.MODEL_FE:
|
||||||
fw_filename = models[selected_model][4]
|
fw_filename = models[selected_model][4]
|
||||||
|
|
||||||
@@ -2098,6 +2260,42 @@ def main():
|
|||||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
|
||||||
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
|
||||||
]
|
]
|
||||||
|
elif fw_filename == "rnode_firmware_tbeam_sx1262.zip":
|
||||||
|
if numeric_version >= 1.55:
|
||||||
|
return [
|
||||||
|
sys.executable, flasher,
|
||||||
|
"--chip", "esp32",
|
||||||
|
"--port", args.port,
|
||||||
|
"--baud", args.baud_flash,
|
||||||
|
"--before", "default_reset",
|
||||||
|
"--after", "hard_reset",
|
||||||
|
"write_flash", "-z",
|
||||||
|
"--flash_mode", "dio",
|
||||||
|
"--flash_freq", "80m",
|
||||||
|
"--flash_size", "4MB",
|
||||||
|
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.boot_app0",
|
||||||
|
"0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bootloader",
|
||||||
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bin",
|
||||||
|
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
|
||||||
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.partitions",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return [
|
||||||
|
sys.executable, flasher,
|
||||||
|
"--chip", "esp32",
|
||||||
|
"--port", args.port,
|
||||||
|
"--baud", args.baud_flash,
|
||||||
|
"--before", "default_reset",
|
||||||
|
"--after", "hard_reset",
|
||||||
|
"write_flash", "-z",
|
||||||
|
"--flash_mode", "dio",
|
||||||
|
"--flash_freq", "80m",
|
||||||
|
"--flash_size", "4MB",
|
||||||
|
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.boot_app0",
|
||||||
|
"0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bootloader",
|
||||||
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
|
||||||
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
|
||||||
|
]
|
||||||
elif fw_filename == "rnode_firmware_lora32v10.zip":
|
elif fw_filename == "rnode_firmware_lora32v10.zip":
|
||||||
if numeric_version >= 1.59:
|
if numeric_version >= 1.59:
|
||||||
return [
|
return [
|
||||||
@@ -2206,6 +2404,24 @@ def main():
|
|||||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin",
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin",
|
||||||
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.partitions",
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.partitions",
|
||||||
]
|
]
|
||||||
|
elif fw_filename == "rnode_firmware_lora32v21_tcxo.zip":
|
||||||
|
return [
|
||||||
|
sys.executable, flasher,
|
||||||
|
"--chip", "esp32",
|
||||||
|
"--port", args.port,
|
||||||
|
"--baud", args.baud_flash,
|
||||||
|
"--before", "default_reset",
|
||||||
|
"--after", "hard_reset",
|
||||||
|
"write_flash", "-z",
|
||||||
|
"--flash_mode", "dio",
|
||||||
|
"--flash_freq", "80m",
|
||||||
|
"--flash_size", "4MB",
|
||||||
|
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.boot_app0",
|
||||||
|
"0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bootloader",
|
||||||
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bin",
|
||||||
|
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
|
||||||
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.partitions",
|
||||||
|
]
|
||||||
elif fw_filename == "rnode_firmware_heltec32v2.zip":
|
elif fw_filename == "rnode_firmware_heltec32v2.zip":
|
||||||
if numeric_version >= 1.55:
|
if numeric_version >= 1.55:
|
||||||
return [
|
return [
|
||||||
@@ -2242,6 +2458,23 @@ def main():
|
|||||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bin",
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bin",
|
||||||
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.partitions",
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.partitions",
|
||||||
]
|
]
|
||||||
|
elif fw_filename == "rnode_firmware_heltec32v3.zip":
|
||||||
|
return [
|
||||||
|
sys.executable, flasher,
|
||||||
|
"--chip", "esp32-s3",
|
||||||
|
"--port", args.port,
|
||||||
|
"--baud", args.baud_flash,
|
||||||
|
"--before", "default_reset",
|
||||||
|
"--after", "hard_reset",
|
||||||
|
"write_flash", "-z",
|
||||||
|
"--flash_mode", "dio",
|
||||||
|
"--flash_freq", "80m",
|
||||||
|
"--flash_size", "8MB",
|
||||||
|
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.boot_app0",
|
||||||
|
"0x0", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bootloader",
|
||||||
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bin",
|
||||||
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.partitions",
|
||||||
|
]
|
||||||
elif fw_filename == "rnode_firmware_featheresp32.zip":
|
elif fw_filename == "rnode_firmware_featheresp32.zip":
|
||||||
if numeric_version >= 1.55:
|
if numeric_version >= 1.55:
|
||||||
return [
|
return [
|
||||||
@@ -2386,6 +2619,24 @@ def main():
|
|||||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin",
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin",
|
||||||
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.partitions",
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.partitions",
|
||||||
]
|
]
|
||||||
|
elif fw_filename == "rnode_firmware_t3s3.zip":
|
||||||
|
return [
|
||||||
|
sys.executable, flasher,
|
||||||
|
"--chip", "esp32s3",
|
||||||
|
"--port", args.port,
|
||||||
|
"--baud", args.baud_flash,
|
||||||
|
"--before", "default_reset",
|
||||||
|
"--after", "hard_reset",
|
||||||
|
"write_flash", "-z",
|
||||||
|
"--flash_mode", "dio",
|
||||||
|
"--flash_freq", "80m",
|
||||||
|
"--flash_size", "4MB",
|
||||||
|
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.boot_app0",
|
||||||
|
"0x0", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bootloader",
|
||||||
|
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bin",
|
||||||
|
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
|
||||||
|
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.partitions",
|
||||||
|
]
|
||||||
elif fw_filename == "extracted_rnode_firmware.zip":
|
elif fw_filename == "extracted_rnode_firmware.zip":
|
||||||
return [
|
return [
|
||||||
sys.executable, flasher,
|
sys.executable, flasher,
|
||||||
@@ -2521,6 +2772,7 @@ def main():
|
|||||||
if args.eeprom_wipe:
|
if args.eeprom_wipe:
|
||||||
RNS.log("WARNING: EEPROM is being wiped! Power down device NOW if you do not want this!")
|
RNS.log("WARNING: EEPROM is being wiped! Power down device NOW if you do not want this!")
|
||||||
rnode.wipe_eeprom()
|
rnode.wipe_eeprom()
|
||||||
|
rnode.hard_reset()
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
RNS.log("Reading EEPROM...")
|
RNS.log("Reading EEPROM...")
|
||||||
@@ -2770,6 +3022,7 @@ def main():
|
|||||||
RNS.log("\tFirmware version : "+rnode.version)
|
RNS.log("\tFirmware version : "+rnode.version)
|
||||||
RNS.log("\tHardware revision : "+str(int(rnode.hw_rev)))
|
RNS.log("\tHardware revision : "+str(int(rnode.hw_rev)))
|
||||||
RNS.log("\tSerial number : "+RNS.hexrep(rnode.serialno))
|
RNS.log("\tSerial number : "+RNS.hexrep(rnode.serialno))
|
||||||
|
RNS.log("\tModem chip : "+str(models[rnode.model][5]))
|
||||||
RNS.log("\tFrequency range : "+str(rnode.min_freq/1e6)+" MHz - "+str(rnode.max_freq/1e6)+" MHz")
|
RNS.log("\tFrequency range : "+str(rnode.min_freq/1e6)+" MHz - "+str(rnode.max_freq/1e6)+" MHz")
|
||||||
RNS.log("\tMax TX power : "+str(rnode.max_output)+" dBm")
|
RNS.log("\tMax TX power : "+str(rnode.max_output)+" dBm")
|
||||||
RNS.log("\tManufactured : "+timestring)
|
RNS.log("\tManufactured : "+timestring)
|
||||||
@@ -2845,24 +3098,39 @@ def main():
|
|||||||
if args.product != None:
|
if args.product != None:
|
||||||
if args.product == "03":
|
if args.product == "03":
|
||||||
mapped_product = ROM.PRODUCT_RNODE
|
mapped_product = ROM.PRODUCT_RNODE
|
||||||
if args.product == "f0":
|
elif args.product == "f0":
|
||||||
mapped_product = ROM.PRODUCT_HMBRW
|
mapped_product = ROM.PRODUCT_HMBRW
|
||||||
if args.product == "e0":
|
elif args.product == "e0":
|
||||||
mapped_product = ROM.PRODUCT_TBEAM
|
mapped_product = ROM.PRODUCT_TBEAM
|
||||||
|
else:
|
||||||
|
if len(args.product) == 2:
|
||||||
|
mapped_product = ord(bytes.fromhex(args.product))
|
||||||
|
|
||||||
if mapped_model != None:
|
if mapped_model != None:
|
||||||
|
if mapped_model == ROM.MODEL_B4_TCXO:
|
||||||
|
model = ROM.MODEL_B4
|
||||||
|
elif mapped_model == ROM.MODEL_B9_TCXO:
|
||||||
|
model = ROM.MODEL_B9
|
||||||
|
else:
|
||||||
model = mapped_model
|
model = mapped_model
|
||||||
else:
|
else:
|
||||||
if args.model == "a4":
|
if args.model == "a4":
|
||||||
model = ROM.MODEL_A4
|
model = ROM.MODEL_A4
|
||||||
elif args.model == "a9":
|
elif args.model == "a9":
|
||||||
model = ROM.MODEL_A9
|
model = ROM.MODEL_A9
|
||||||
|
elif args.model == "a1":
|
||||||
|
model = ROM.MODEL_A1
|
||||||
|
elif args.model == "a6":
|
||||||
|
model = ROM.MODEL_A6
|
||||||
elif args.model == "e4":
|
elif args.model == "e4":
|
||||||
model = ROM.MODEL_E4
|
model = ROM.MODEL_E4
|
||||||
elif args.model == "e9":
|
elif args.model == "e9":
|
||||||
model = ROM.MODEL_E9
|
model = ROM.MODEL_E9
|
||||||
elif args.model == "ff":
|
elif args.model == "ff":
|
||||||
model = ROM.MODEL_FF
|
model = ROM.MODEL_FF
|
||||||
|
else:
|
||||||
|
if len(args.model) == 2:
|
||||||
|
model = ord(bytes.fromhex(args.model))
|
||||||
|
|
||||||
|
|
||||||
if args.hwrev != None and (args.hwrev > 0 and args.hwrev < 256):
|
if args.hwrev != None and (args.hwrev > 0 and args.hwrev < 256):
|
||||||
@@ -2920,6 +3188,10 @@ def main():
|
|||||||
RNS.log("No signing key found")
|
RNS.log("No signing key found")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
if model == ROM.MODEL_A1 or model == ROM.MODEL_A6:
|
||||||
|
rnode.hard_reset()
|
||||||
|
RNS.log("Waiting for ESP32 reset...")
|
||||||
|
time.sleep(6.5)
|
||||||
|
|
||||||
RNS.log("Bootstrapping device EEPROM...")
|
RNS.log("Bootstrapping device EEPROM...")
|
||||||
|
|
||||||
@@ -2972,6 +3244,8 @@ def main():
|
|||||||
partition_hash = get_partition_hash(UPD_DIR+"/"+selected_version+"/"+partition_filename)
|
partition_hash = get_partition_hash(UPD_DIR+"/"+selected_version+"/"+partition_filename)
|
||||||
|
|
||||||
if partition_hash != None:
|
if partition_hash != None:
|
||||||
|
time.sleep(0.75)
|
||||||
|
RNS.log("Setting firmware checksum...")
|
||||||
rnode.set_firmware_hash(partition_hash)
|
rnode.set_firmware_hash(partition_hash)
|
||||||
|
|
||||||
rnode.hard_reset()
|
rnode.hard_reset()
|
||||||
|
|||||||
+35
-10
@@ -31,9 +31,9 @@ import argparse
|
|||||||
from RNS._version import __version__
|
from RNS._version import __version__
|
||||||
|
|
||||||
DEFAULT_PROBE_SIZE = 16
|
DEFAULT_PROBE_SIZE = 16
|
||||||
DEFAULT_TIMEOUT = 15
|
DEFAULT_TIMEOUT = 12
|
||||||
|
|
||||||
def program_setup(configdir, destination_hexhash, size=None, full_name = None, verbosity = 0, timeout=None):
|
def program_setup(configdir, destination_hexhash, size=None, full_name = None, verbosity = 0, timeout=None, wait=0, probes=1):
|
||||||
if size == None: size = DEFAULT_PROBE_SIZE
|
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")
|
||||||
@@ -73,7 +73,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
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)
|
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
|
||||||
i = 0
|
i = 0
|
||||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||||
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
|
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
|
||||||
@@ -96,6 +96,13 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
*aspects
|
*aspects
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sent = 0
|
||||||
|
replies = 0
|
||||||
|
while probes:
|
||||||
|
|
||||||
|
if sent > 0:
|
||||||
|
time.sleep(wait)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
probe = RNS.Packet(request_destination, os.urandom(size))
|
probe = RNS.Packet(request_destination, os.urandom(size))
|
||||||
probe.pack()
|
probe.pack()
|
||||||
@@ -104,6 +111,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
exit(3)
|
exit(3)
|
||||||
|
|
||||||
receipt = probe.send()
|
receipt = probe.send()
|
||||||
|
sent += 1
|
||||||
|
|
||||||
if more_output:
|
if more_output:
|
||||||
nhd = reticulum.get_next_hop(destination_hash)
|
nhd = reticulum.get_next_hop(destination_hash)
|
||||||
@@ -113,9 +121,9 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
else:
|
else:
|
||||||
more = ""
|
more = ""
|
||||||
|
|
||||||
print("\rSent "+str(size)+" byte probe to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
|
print("\rSent probe "+str(sent)+" ("+str(size)+" bytes) to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
|
||||||
|
|
||||||
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT)
|
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
|
||||||
i = 0
|
i = 0
|
||||||
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
|
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
@@ -125,12 +133,13 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
|
|
||||||
if time.time() > _timeout:
|
if time.time() > _timeout:
|
||||||
print("\r \rProbe timed out")
|
print("\r \rProbe timed out")
|
||||||
exit(2)
|
|
||||||
|
|
||||||
|
else:
|
||||||
print("\b\b ")
|
print("\b\b ")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||||
|
replies += 1
|
||||||
hops = RNS.Transport.hops_to(destination_hash)
|
hops = RNS.Transport.hops_to(destination_hash)
|
||||||
if hops != 1:
|
if hops != 1:
|
||||||
ms = "s"
|
ms = "s"
|
||||||
@@ -149,6 +158,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
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)
|
||||||
|
reception_q = reticulum.get_packet_q(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]"
|
||||||
@@ -156,6 +166,9 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
||||||
|
|
||||||
|
if reception_q != None:
|
||||||
|
reception_stats += " [Link Quality "+str(reception_q)+"%]"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if receipt.proof_packet != None:
|
if receipt.proof_packet != None:
|
||||||
if receipt.proof_packet.rssi != None:
|
if receipt.proof_packet.rssi != None:
|
||||||
@@ -165,17 +178,24 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
|||||||
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"Valid reply received from "+
|
"Valid reply from "+
|
||||||
RNS.prettyhexrep(receipt.destination.hash)+
|
RNS.prettyhexrep(receipt.destination.hash)+
|
||||||
"\nRound-trip time is "+rttstring+
|
"\nRound-trip time is "+rttstring+
|
||||||
" over "+str(hops)+" hop"+ms+
|
" over "+str(hops)+" hop"+ms+
|
||||||
reception_stats
|
reception_stats+"\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("\r \rProbe timed out")
|
print("\r \rProbe timed out")
|
||||||
exit(2)
|
|
||||||
|
|
||||||
|
probes -= 1
|
||||||
|
|
||||||
|
loss = round((1-(replies/sent))*100, 2)
|
||||||
|
print(f"Sent {sent}, received {replies}, packet loss {loss}%")
|
||||||
|
if loss > 0:
|
||||||
|
exit(2)
|
||||||
|
else:
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -184,7 +204,9 @@ def main():
|
|||||||
|
|
||||||
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||||
parser.add_argument("-s", "--size", action="store", default=None, help="size of probe packet payload in bytes", type=int)
|
parser.add_argument("-s", "--size", action="store", default=None, help="size of probe packet payload in bytes", type=int)
|
||||||
|
parser.add_argument("-n", "--probes", action="store", default=1, help="number of probes to send", type=int)
|
||||||
parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float)
|
parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float)
|
||||||
|
parser.add_argument("-w", "--wait", metavar="seconds", action="store", default=0, help="time between each probe", type=float)
|
||||||
parser.add_argument("--version", action="version", version="rnprobe {version}".format(version=__version__))
|
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("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("destination_hash", nargs="?", default=None, help="hexadecimal hash of the destination", type=str)
|
||||||
@@ -208,7 +230,10 @@ def main():
|
|||||||
destination_hexhash = args.destination_hash,
|
destination_hexhash = args.destination_hash,
|
||||||
size = args.size,
|
size = args.size,
|
||||||
full_name = args.full_name,
|
full_name = args.full_name,
|
||||||
verbosity = args.verbose
|
verbosity = args.verbose,
|
||||||
|
probes = args.probes,
|
||||||
|
wait = args.wait,
|
||||||
|
timeout = args.timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|||||||
+38
-5
@@ -144,6 +144,12 @@ def rand():
|
|||||||
result = instance_random.random()
|
result = instance_random.random()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def trace_exception(e):
|
||||||
|
import traceback
|
||||||
|
exception_info = "".join(traceback.TracebackException.from_exception(e).format())
|
||||||
|
log(f"An unhandled {str(type(e))} exception occurred: {str(e)}", LOG_ERROR)
|
||||||
|
log(exception_info, LOG_ERROR)
|
||||||
|
|
||||||
def hexrep(data, delimit=True):
|
def hexrep(data, delimit=True):
|
||||||
try:
|
try:
|
||||||
iter(data)
|
iter(data)
|
||||||
@@ -161,6 +167,9 @@ def prettyhexrep(data):
|
|||||||
hexrep = "<"+delimiter.join("{:02x}".format(c) for c in data)+">"
|
hexrep = "<"+delimiter.join("{:02x}".format(c) for c in data)+">"
|
||||||
return hexrep
|
return hexrep
|
||||||
|
|
||||||
|
def prettyspeed(num, suffix="b"):
|
||||||
|
return prettysize(num/8, suffix=suffix)+"ps"
|
||||||
|
|
||||||
def prettysize(num, suffix='B'):
|
def prettysize(num, suffix='B'):
|
||||||
units = ['','K','M','G','T','P','E','Z']
|
units = ['','K','M','G','T','P','E','Z']
|
||||||
last_unit = 'Y'
|
last_unit = 'Y'
|
||||||
@@ -192,13 +201,32 @@ def prettyfrequency(hz, suffix="Hz"):
|
|||||||
|
|
||||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
def prettytime(time, verbose=False):
|
def prettydistance(m, suffix="m"):
|
||||||
|
num = m*1e6
|
||||||
|
units = ["µ", "m", "c", ""]
|
||||||
|
last_unit = "K"
|
||||||
|
|
||||||
|
for unit in units:
|
||||||
|
divisor = 1000.0
|
||||||
|
if unit == "m": divisor = 10
|
||||||
|
if unit == "c": divisor = 100
|
||||||
|
|
||||||
|
if abs(num) < divisor:
|
||||||
|
return "%.2f %s%s" % (num, unit, suffix)
|
||||||
|
num /= divisor
|
||||||
|
|
||||||
|
return "%.2f %s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
|
def prettytime(time, verbose=False, compact=False):
|
||||||
days = int(time // (24 * 3600))
|
days = int(time // (24 * 3600))
|
||||||
time = time % (24 * 3600)
|
time = time % (24 * 3600)
|
||||||
hours = int(time // 3600)
|
hours = int(time // 3600)
|
||||||
time %= 3600
|
time %= 3600
|
||||||
minutes = int(time // 60)
|
minutes = int(time // 60)
|
||||||
time %= 60
|
time %= 60
|
||||||
|
if compact:
|
||||||
|
seconds = int(time)
|
||||||
|
else:
|
||||||
seconds = round(time, 2)
|
seconds = round(time, 2)
|
||||||
|
|
||||||
ss = "" if seconds == 1 else "s"
|
ss = "" if seconds == 1 else "s"
|
||||||
@@ -206,18 +234,23 @@ def prettytime(time, verbose=False):
|
|||||||
sh = "" if hours == 1 else "s"
|
sh = "" if hours == 1 else "s"
|
||||||
sd = "" if days == 1 else "s"
|
sd = "" if days == 1 else "s"
|
||||||
|
|
||||||
|
displayed = 0
|
||||||
components = []
|
components = []
|
||||||
if days > 0:
|
if days > 0 and ((not compact) or displayed < 2):
|
||||||
components.append(str(days)+" day"+sd if verbose else str(days)+"d")
|
components.append(str(days)+" day"+sd if verbose else str(days)+"d")
|
||||||
|
displayed += 1
|
||||||
|
|
||||||
if hours > 0:
|
if hours > 0 and ((not compact) or displayed < 2):
|
||||||
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h")
|
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h")
|
||||||
|
displayed += 1
|
||||||
|
|
||||||
if minutes > 0:
|
if minutes > 0 and ((not compact) or displayed < 2):
|
||||||
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m")
|
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m")
|
||||||
|
displayed += 1
|
||||||
|
|
||||||
if seconds > 0:
|
if seconds > 0 and ((not compact) or displayed < 2):
|
||||||
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
|
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
|
||||||
|
displayed += 1
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
tstr = ""
|
tstr = ""
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
__version__ = "0.6.2"
|
__version__ = "0.7.4"
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ 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.6.x` release cycle aims at completing
|
- The current `0.7.x` release cycle aims at completing
|
||||||
- [ ] Overhauling and updating the documentation
|
- [ ] Overhauling and updating the documentation
|
||||||
- [ ] Distributed Destination Naming System
|
- [ ] Distributed Destination Naming System
|
||||||
- [ ] Create a standalone RNS Daemon app for Android
|
- [ ] Create a standalone RNS Daemon app for Android
|
||||||
|
|||||||
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: 46393e4581f8a313437c181a16088a4d
|
config: 541171817b95d649201365804faf729c
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ locally on your device using the following command:
|
|||||||
|
|
||||||
It is also possible to include Reticulum in apps compiled and distributed as
|
It is also possible to include Reticulum in apps compiled and distributed as
|
||||||
Android APKs. A detailed tutorial and example source code will be included
|
Android APKs. A detailed tutorial and example source code will be included
|
||||||
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and startig point.
|
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and starting point.
|
||||||
|
|
||||||
|
|
||||||
ARM64
|
ARM64
|
||||||
|
|||||||
@@ -33,9 +33,20 @@ system, which should be enabled by default in almost all OSes.
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
# This example demonstrates a TCP server interface.
|
# This example demonstrates a bare-minimum setup
|
||||||
# It will listen for incoming connections on the
|
# of an Auto Interface. It will allow communica-
|
||||||
# specified IP address and port number.
|
# tion with all other reachable devices on all
|
||||||
|
# usable physical ethernet-based devices that
|
||||||
|
# are available on the system.
|
||||||
|
|
||||||
|
[[Default Interface]]
|
||||||
|
type = AutoInterface
|
||||||
|
interface_enabled = True
|
||||||
|
|
||||||
|
# This example demonstrates an more specifically
|
||||||
|
# configured Auto Interface, that only uses spe-
|
||||||
|
# cific physical interfaces, and has a number of
|
||||||
|
# other configuration options set.
|
||||||
|
|
||||||
[[Default Interface]]
|
[[Default Interface]]
|
||||||
type = AutoInterface
|
type = AutoInterface
|
||||||
@@ -47,6 +58,12 @@ system, which should be enabled by default in almost all OSes.
|
|||||||
|
|
||||||
group_id = reticulum
|
group_id = reticulum
|
||||||
|
|
||||||
|
# You can also choose the multicast address type:
|
||||||
|
# temporary (default, Temporary Multicast Address)
|
||||||
|
# or permanent (Permanent Multicast Address)
|
||||||
|
|
||||||
|
multicast_address_type = permanent
|
||||||
|
|
||||||
# You can also select specifically which
|
# You can also select specifically which
|
||||||
# kernel networking devices to use.
|
# kernel networking devices to use.
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ guide the design of Reticulum:
|
|||||||
it can be easily modified and replicated by anyone interested in doing so.
|
it can be easily modified and replicated by anyone interested in doing so.
|
||||||
* **Very low bandwidth requirements**
|
* **Very low bandwidth requirements**
|
||||||
Reticulum should be able to function reliably over links with a transmission capacity as low
|
Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||||
as *500 bits per second*.
|
as *5 bits per second*.
|
||||||
* **Encryption by default**
|
* **Encryption by default**
|
||||||
Reticulum must use strong encryption by default for all communication.
|
Reticulum must use strong encryption by default for all communication.
|
||||||
* **Initiator Anonymity**
|
* **Initiator Anonymity**
|
||||||
@@ -595,7 +595,7 @@ or less any medium that allows you to send and receive data, which satisfies som
|
|||||||
minimum requirements.
|
minimum requirements.
|
||||||
|
|
||||||
The communication channel must support at least half-duplex operation, and provide an average
|
The communication channel must support at least half-duplex operation, and provide an average
|
||||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes. The
|
||||||
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
||||||
runtime environment.
|
runtime environment.
|
||||||
|
|
||||||
|
|||||||
@@ -780,6 +780,9 @@ Reticulum as a System Service
|
|||||||
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
|
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
|
||||||
service and have it start automatically at boot.
|
service and have it start automatically at boot.
|
||||||
|
|
||||||
|
Systemwide Service
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
If you installed Reticulum with ``pip``, the ``rnsd`` program will most likely
|
If you installed Reticulum with ``pip``, the ``rnsd`` program will most likely
|
||||||
be located in a user-local installation path only, which means ``systemd`` will not
|
be located in a user-local installation path only, which means ``systemd`` will not
|
||||||
be able to execute it. In this case, you can simply symlink the ``rnsd`` program
|
be able to execute it. In this case, you can simply symlink the ``rnsd`` program
|
||||||
@@ -827,3 +830,48 @@ If you want to automatically start ``rnsd`` at boot, run:
|
|||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
sudo systemctl enable rnsd
|
sudo systemctl enable rnsd
|
||||||
|
|
||||||
|
Userspace Service
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Alternatively you can use a user systemd service instead of a system wide one. This way the whole setup can be done as a regular user.
|
||||||
|
Create a user systemd service files ``~/.config/systemd/user/rnsd.service`` with the following content:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Reticulum Network Stack Daemon
|
||||||
|
After=default.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
# If you run Reticulum on WiFi devices,
|
||||||
|
# or other devices that need some extra
|
||||||
|
# time to initialise, you might want to
|
||||||
|
# add a short delay before Reticulum is
|
||||||
|
# started by systemd:
|
||||||
|
# ExecStartPre=/bin/sleep 10
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
ExecStart=RNS_BIN_DIR/rnsd --service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
||||||
|
Replace ``RNS_BIN_DIR`` with the path to your Reticulum binary directory (eg. /home/USERNAMEHERE/rns/bin).
|
||||||
|
|
||||||
|
Start user service:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user start rnsd.service
|
||||||
|
|
||||||
|
If you want to automatically start ``rnsd`` without having to log in as the USERNAMEHERE, do:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
sudo loginctl enable-linger USERNAMEHERE
|
||||||
|
systemctl --user enable rnsd.service
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ What does Reticulum Offer?
|
|||||||
Where can Reticulum be Used?
|
Where can Reticulum be Used?
|
||||||
============================
|
============================
|
||||||
Over practically any medium that can support at least a half-duplex channel
|
Over practically any medium that can support at least a half-duplex channel
|
||||||
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
|
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
|
||||||
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
||||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||||
of the types of interfaces Reticulum was designed for.
|
of the types of interfaces Reticulum was designed for.
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* _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-2023 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -324,7 +324,6 @@ aside.sidebar {
|
|||||||
p.sidebar-title {
|
p.sidebar-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.contents,
|
nav.contents,
|
||||||
aside.topic,
|
aside.topic,
|
||||||
div.admonition, div.topic, blockquote {
|
div.admonition, div.topic, blockquote {
|
||||||
@@ -332,7 +331,6 @@ div.admonition, div.topic, blockquote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* -- topics ---------------------------------------------------------------- */
|
/* -- topics ---------------------------------------------------------------- */
|
||||||
|
|
||||||
nav.contents,
|
nav.contents,
|
||||||
aside.topic,
|
aside.topic,
|
||||||
div.topic {
|
div.topic {
|
||||||
@@ -608,7 +606,6 @@ ol.simple p,
|
|||||||
ul.simple p {
|
ul.simple p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside.footnote > span,
|
aside.footnote > span,
|
||||||
div.citation > span {
|
div.citation > span {
|
||||||
float: left;
|
float: left;
|
||||||
@@ -670,16 +667,6 @@ 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;
|
||||||
@@ -748,14 +735,6 @@ 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 {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Base JavaScript utilities for all Sphinx HTML documentation.
|
* Base JavaScript utilities for all Sphinx HTML documentation.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2022 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.6.2 beta',
|
VERSION: '0.7.4 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-2023 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Sphinx JavaScript utilities for the full-text search.
|
* Sphinx JavaScript utilities for the full-text search.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+43
-17
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Code Examples - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Code Examples - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -656,6 +656,7 @@ the Packet interface.</p>
|
|||||||
<span class="c1"># of the packet. #</span>
|
<span class="c1"># of the packet. #</span>
|
||||||
<span class="c1">##########################################################</span>
|
<span class="c1">##########################################################</span>
|
||||||
|
|
||||||
|
<span class="kn">import</span> <span class="nn">os</span>
|
||||||
<span class="kn">import</span> <span class="nn">argparse</span>
|
<span class="kn">import</span> <span class="nn">argparse</span>
|
||||||
<span class="kn">import</span> <span class="nn">RNS</span>
|
<span class="kn">import</span> <span class="nn">RNS</span>
|
||||||
|
|
||||||
@@ -678,8 +679,19 @@ the Packet interface.</p>
|
|||||||
<span class="c1"># We must first initialise Reticulum</span>
|
<span class="c1"># We must first initialise Reticulum</span>
|
||||||
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># Randomly create a new identity for our echo server</span>
|
<span class="c1"># Load identity from file if it exist or randomley create</span>
|
||||||
|
<span class="k">if</span> <span class="n">configpath</span><span class="p">:</span>
|
||||||
|
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">/storage/identitiesy/</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">configpath</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">/storage/identities/</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">configdir</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">):</span>
|
||||||
|
<span class="c1"># Load identity from file</span>
|
||||||
|
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">from_file</span><span class="p">(</span><span class="n">ifilepath</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">"loaded identity from file: "</span><span class="o">+</span><span class="n">ifilepath</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="c1"># Randomly create a new identity for our echo example</span>
|
||||||
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
|
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</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">"created new identity"</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># We create a destination that clients can query. We want</span>
|
<span class="c1"># We create a destination that clients can query. We want</span>
|
||||||
<span class="c1"># to be able to verify echo replies to our clients, so we</span>
|
<span class="c1"># to be able to verify echo replies to our clients, so we</span>
|
||||||
@@ -1018,8 +1030,20 @@ destination, and passing traffic back and forth over the link.</p>
|
|||||||
<span class="c1"># We must first initialise Reticulum</span>
|
<span class="c1"># We must first initialise Reticulum</span>
|
||||||
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="c1"># Load identity from file if it exist or randomley create</span>
|
||||||
|
<span class="k">if</span> <span class="n">configpath</span><span class="p">:</span>
|
||||||
|
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">/storage/identitiesy/</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">configpath</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">/storage/identities/</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">configdir</span><span class="p">,</span><span class="n">APP_NAME</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">"ifilepath: </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="n">ifilepath</span><span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">):</span>
|
||||||
|
<span class="c1"># Load identity from file</span>
|
||||||
|
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">from_file</span><span class="p">(</span><span class="n">ifilepath</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">"loaded identity from file: "</span><span class="o">+</span><span class="n">ifilepath</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
<span class="c1"># Randomly create a new identity for our link example</span>
|
<span class="c1"># Randomly create a new identity for our link example</span>
|
||||||
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
|
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</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">"created new identity"</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># We create a destination that clients can connect to. We</span>
|
<span class="c1"># We create a destination that clients can connect to. We</span>
|
||||||
<span class="c1"># want clients to create links to this destination, so we</span>
|
<span class="c1"># want clients to create links to this destination, so we</span>
|
||||||
@@ -3084,8 +3108,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
|
|||||||
<span class="c1"># And set up a small job to check for</span>
|
<span class="c1"># And set up a small job to check for</span>
|
||||||
<span class="c1"># a potential timeout in receiving the</span>
|
<span class="c1"># a potential timeout in receiving the</span>
|
||||||
<span class="c1"># file list</span>
|
<span class="c1"># file list</span>
|
||||||
<span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">filelist_timeout_job</span><span class="p">)</span>
|
<span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">filelist_timeout_job</span><span class="p">,</span> <span class="n">daemon</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||||
<span class="n">thread</span><span class="o">.</span><span class="n">setDaemon</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span>
|
|
||||||
<span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
|
<span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
|
||||||
|
|
||||||
<span class="c1"># This job just sleeps for the specified</span>
|
<span class="c1"># This job just sleeps for the specified</span>
|
||||||
@@ -3322,11 +3345,14 @@ 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?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+17
-14
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -257,11 +257,14 @@
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+31
-14
@@ -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-7.1.2, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -159,13 +159,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -373,6 +373,8 @@
|
|||||||
<li><a href="reference.html#RNS.Link.get_establishment_rate">get_establishment_rate() (RNS.Link method)</a>
|
<li><a href="reference.html#RNS.Link.get_establishment_rate">get_establishment_rate() (RNS.Link method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Resource.get_hash">get_hash() (RNS.Resource method)</a>
|
<li><a href="reference.html#RNS.Resource.get_hash">get_hash() (RNS.Resource method)</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="reference.html#RNS.Reticulum.get_instance">get_instance() (RNS.Reticulum static method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Resource.get_parts">get_parts() (RNS.Resource method)</a>
|
<li><a href="reference.html#RNS.Resource.get_parts">get_parts() (RNS.Resource method)</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -389,6 +391,8 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
<li><a href="reference.html#RNS.Identity.get_public_key">get_public_key() (RNS.Identity method)</a>
|
<li><a href="reference.html#RNS.Identity.get_public_key">get_public_key() (RNS.Identity method)</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="reference.html#RNS.Link.get_q">get_q() (RNS.Link method)</a>
|
||||||
</li>
|
</li>
|
||||||
</ul></td>
|
</ul></td>
|
||||||
<td style="width: 33%; vertical-align: top;"><ul>
|
<td style="width: 33%; vertical-align: top;"><ul>
|
||||||
@@ -401,10 +405,14 @@
|
|||||||
<li><a href="reference.html#RNS.RequestReceipt.get_response">get_response() (RNS.RequestReceipt method)</a>
|
<li><a href="reference.html#RNS.RequestReceipt.get_response">get_response() (RNS.RequestReceipt method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.RequestReceipt.get_response_time">get_response_time() (RNS.RequestReceipt method)</a>
|
<li><a href="reference.html#RNS.RequestReceipt.get_response_time">get_response_time() (RNS.RequestReceipt method)</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="reference.html#RNS.Link.get_rssi">get_rssi() (RNS.Link method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
|
<li><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Resource.get_segments">get_segments() (RNS.Resource method)</a>
|
<li><a href="reference.html#RNS.Resource.get_segments">get_segments() (RNS.Resource method)</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="reference.html#RNS.Link.get_snr">get_snr() (RNS.Link method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
|
<li><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
|
||||||
|
|
||||||
@@ -502,6 +510,8 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul></td>
|
</ul></td>
|
||||||
<td style="width: 33%; vertical-align: top;"><ul>
|
<td style="width: 33%; vertical-align: top;"><ul>
|
||||||
|
<li><a href="reference.html#RNS.Reticulum.MINIMUM_BITRATE">MINIMUM_BITRATE (RNS.Reticulum attribute)</a>
|
||||||
|
</li>
|
||||||
<li><a href="reference.html#RNS.MessageBase.MSGTYPE">MSGTYPE (RNS.MessageBase attribute)</a>
|
<li><a href="reference.html#RNS.MessageBase.MSGTYPE">MSGTYPE (RNS.MessageBase attribute)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Reticulum.MTU">MTU (RNS.Reticulum attribute)</a>
|
<li><a href="reference.html#RNS.Reticulum.MTU">MTU (RNS.Reticulum attribute)</a>
|
||||||
@@ -520,6 +530,8 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul></td>
|
</ul></td>
|
||||||
<td style="width: 33%; vertical-align: top;"><ul>
|
<td style="width: 33%; vertical-align: top;"><ul>
|
||||||
|
<li><a href="reference.html#RNS.Link.no_data_for">no_data_for() (RNS.Link method)</a>
|
||||||
|
</li>
|
||||||
<li><a href="reference.html#RNS.Link.no_inbound_for">no_inbound_for() (RNS.Link method)</a>
|
<li><a href="reference.html#RNS.Link.no_inbound_for">no_inbound_for() (RNS.Link method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Link.no_outbound_for">no_outbound_for() (RNS.Link method)</a>
|
<li><a href="reference.html#RNS.Link.no_outbound_for">no_outbound_for() (RNS.Link method)</a>
|
||||||
@@ -656,10 +668,12 @@
|
|||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Identity.to_file">to_file() (RNS.Identity method)</a>
|
<li><a href="reference.html#RNS.Identity.to_file">to_file() (RNS.Identity method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Transport">Transport (class in RNS)</a>
|
<li><a href="reference.html#RNS.Link.track_phy_stats">track_phy_stats() (RNS.Link method)</a>
|
||||||
</li>
|
</li>
|
||||||
</ul></td>
|
</ul></td>
|
||||||
<td style="width: 33%; vertical-align: top;"><ul>
|
<td style="width: 33%; vertical-align: top;"><ul>
|
||||||
|
<li><a href="reference.html#RNS.Transport">Transport (class in RNS)</a>
|
||||||
|
</li>
|
||||||
<li><a href="reference.html#RNS.Reticulum.transport_enabled">transport_enabled() (RNS.Reticulum static method)</a>
|
<li><a href="reference.html#RNS.Reticulum.transport_enabled">transport_enabled() (RNS.Reticulum static method)</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a>
|
<li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a>
|
||||||
@@ -723,11 +737,14 @@
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Getting Started Fast - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Getting Started Fast - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -560,7 +560,7 @@ locally on your device using the following command:</p>
|
|||||||
</div>
|
</div>
|
||||||
<p>It is also possible to include Reticulum in apps compiled and distributed as
|
<p>It is also possible to include Reticulum in apps compiled and distributed as
|
||||||
Android APKs. A detailed tutorial and example source code will be included
|
Android APKs. A detailed tutorial and example source code will be included
|
||||||
here at a later point. Until then you can use the <a class="reference external" href="https://github.com/markqvist/sideband">Sideband source code</a> as an example and startig point.</p>
|
here at a later point. Until then you can use the <a class="reference external" href="https://github.com/markqvist/sideband">Sideband source code</a> as an example and starting point.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="arm64">
|
<section id="arm64">
|
||||||
<h3>ARM64<a class="headerlink" href="#arm64" title="Permalink to this heading">#</a></h3>
|
<h3>ARM64<a class="headerlink" href="#arm64" title="Permalink to this heading">#</a></h3>
|
||||||
@@ -758,11 +758,14 @@ section of this manual.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+17
-14
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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="Configuring 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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Communications Hardware - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Communications Hardware - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -519,11 +519,14 @@ can be used with Reticulum. This includes virtual software modems such as
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+17
-14
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="#"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="#"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -468,11 +468,14 @@ 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?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+37
-17
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Configuring Interfaces - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Configuring Interfaces - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -241,9 +241,20 @@ infrastructure like routers or DHCP servers, but will require at least some
|
|||||||
sort of switching medium between peers (a wired switch, a hub, a WiFi access
|
sort of switching medium between peers (a wired switch, a hub, a WiFi access
|
||||||
point or similar), and that link-local IPv6 is enabled in your operating
|
point or similar), and that link-local IPv6 is enabled in your operating
|
||||||
system, which should be enabled by default in almost all OSes.</p>
|
system, which should be enabled by default in almost all OSes.</p>
|
||||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example demonstrates a TCP server interface.</span>
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example demonstrates a bare-minimum setup</span>
|
||||||
<span class="c1"># It will listen for incoming connections on the</span>
|
<span class="c1"># of an Auto Interface. It will allow communica-</span>
|
||||||
<span class="c1"># specified IP address and port number.</span>
|
<span class="c1"># tion with all other reachable devices on all</span>
|
||||||
|
<span class="c1"># usable physical ethernet-based devices that</span>
|
||||||
|
<span class="c1"># are available on the system.</span>
|
||||||
|
|
||||||
|
<span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||||
|
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
|
||||||
|
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||||
|
|
||||||
|
<span class="c1"># This example demonstrates an more specifically</span>
|
||||||
|
<span class="c1"># configured Auto Interface, that only uses spe-</span>
|
||||||
|
<span class="c1"># cific physical interfaces, and has a number of</span>
|
||||||
|
<span class="c1"># other configuration options set.</span>
|
||||||
|
|
||||||
<span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
|
<span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
|
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
|
||||||
@@ -255,6 +266,12 @@ system, which should be enabled by default in almost all OSes.</p>
|
|||||||
|
|
||||||
<span class="n">group_id</span> <span class="o">=</span> <span class="n">reticulum</span>
|
<span class="n">group_id</span> <span class="o">=</span> <span class="n">reticulum</span>
|
||||||
|
|
||||||
|
<span class="c1"># You can also choose the multicast address type:</span>
|
||||||
|
<span class="c1"># temporary (default, Temporary Multicast Address)</span>
|
||||||
|
<span class="c1"># or permanent (Permanent Multicast Address)</span>
|
||||||
|
|
||||||
|
<span class="n">multicast_address_type</span> <span class="o">=</span> <span class="n">permanent</span>
|
||||||
|
|
||||||
<span class="c1"># You can also select specifically which</span>
|
<span class="c1"># You can also select specifically which</span>
|
||||||
<span class="c1"># kernel networking devices to use.</span>
|
<span class="c1"># kernel networking devices to use.</span>
|
||||||
|
|
||||||
@@ -1106,11 +1123,14 @@ to <code class="docutils literal notranslate"><span class="pre">30</span></code>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+17
-14
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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="Configuring 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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Building Networks - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Building Networks - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -467,11 +467,14 @@ 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?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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.
+100
-21
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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="prev" title="Support Reticulum" href="support.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>API Reference - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>API Reference - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -253,7 +253,7 @@ other programs to use on demand.</p>
|
|||||||
<dt class="sig sig-object py" id="RNS.Reticulum.MTU">
|
<dt class="sig sig-object py" id="RNS.Reticulum.MTU">
|
||||||
<span class="sig-name descname"><span class="pre">MTU</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">500</span></em><a class="headerlink" href="#RNS.Reticulum.MTU" title="Permalink to this definition">#</a></dt>
|
<span class="sig-name descname"><span class="pre">MTU</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">500</span></em><a class="headerlink" href="#RNS.Reticulum.MTU" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><p>The MTU that Reticulum adheres to, and will expect other peers to
|
<dd><p>The MTU that Reticulum adheres to, and will expect other peers to
|
||||||
adhere to. By default, the MTU is 507 bytes. In custom RNS network
|
adhere to. By default, the MTU is 500 bytes. In custom RNS network
|
||||||
implementations, it is possible to change this value, but doing so will
|
implementations, it is possible to change this value, but doing so will
|
||||||
completely break compatibility with all other RNS networks. An identical
|
completely break compatibility with all other RNS networks. An identical
|
||||||
MTU is a prerequisite for peers to communicate in the same network.</p>
|
MTU is a prerequisite for peers to communicate in the same network.</p>
|
||||||
@@ -275,7 +275,22 @@ links don’t overwhelm the capacity of smaller networks on slower
|
|||||||
mediums. If an announce remains queued for an extended amount of time,
|
mediums. If an announce remains queued for an extended amount of time,
|
||||||
it will eventually be dropped.</p>
|
it will eventually be dropped.</p>
|
||||||
<p>This value will be applied by default to all created interfaces,
|
<p>This value will be applied by default to all created interfaces,
|
||||||
but it can be configured individually on a per-interface basis.</p>
|
but it can be configured individually on a per-interface basis. In
|
||||||
|
general, the global default setting should not be changed, and any
|
||||||
|
alterations should be made on a per-interface basis instead.</p>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py attribute">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Reticulum.MINIMUM_BITRATE">
|
||||||
|
<span class="sig-name descname"><span class="pre">MINIMUM_BITRATE</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">5</span></em><a class="headerlink" href="#RNS.Reticulum.MINIMUM_BITRATE" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><p>Minimum bitrate required across a medium for Reticulum to be able
|
||||||
|
to successfully establish links. Currently 5 bits per second.</p>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py method">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Reticulum.get_instance">
|
||||||
|
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_instance</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Reticulum.get_instance" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><p>Return the currently running Reticulum instance</p>
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
|
|
||||||
<dl class="py method">
|
<dl class="py method">
|
||||||
@@ -1057,6 +1072,50 @@ thus preserved. This method can be used for authentication.</p>
|
|||||||
</dl>
|
</dl>
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py method">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Link.track_phy_stats">
|
||||||
|
<span class="sig-name descname"><span class="pre">track_phy_stats</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">track</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.track_phy_stats" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><p>You can enable physical layer statistics on a per-link basis. If this is enabled,
|
||||||
|
and the link is running over an interface that supports reporting physical layer
|
||||||
|
statistics, you will be able to retrieve stats such as <em>RSSI</em>, <em>SNR</em> and physical
|
||||||
|
<em>Link Quality</em> for the link.</p>
|
||||||
|
<dl class="field-list simple">
|
||||||
|
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
||||||
|
<dd class="field-odd"><p><strong>track</strong> – Whether or not to keep track of physical layer statistics. Value must be <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py method">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Link.get_rssi">
|
||||||
|
<span class="sig-name descname"><span class="pre">get_rssi</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_rssi" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><dl class="field-list simple">
|
||||||
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
|
<dd class="field-odd"><p>The physical layer <em>Received Signal Strength Indication</em> if available, otherwise <code class="docutils literal notranslate"><span class="pre">None</span></code>. Physical layer statistics must be enabled on the link for this method to return a value.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py method">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Link.get_snr">
|
||||||
|
<span class="sig-name descname"><span class="pre">get_snr</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_snr" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><dl class="field-list simple">
|
||||||
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
|
<dd class="field-odd"><p>The physical layer <em>Signal-to-Noise Ratio</em> if available, otherwise <code class="docutils literal notranslate"><span class="pre">None</span></code>. Physical layer statistics must be enabled on the link for this method to return a value.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py method">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Link.get_q">
|
||||||
|
<span class="sig-name descname"><span class="pre">get_q</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_q" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><dl class="field-list simple">
|
||||||
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
|
<dd class="field-odd"><p>The physical layer <em>Link Quality</em> if available, otherwise <code class="docutils literal notranslate"><span class="pre">None</span></code>. Physical layer statistics must be enabled on the link for this method to return a value.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
<dl class="py method">
|
<dl class="py method">
|
||||||
<dt class="sig sig-object py" id="RNS.Link.get_establishment_rate">
|
<dt class="sig sig-object py" id="RNS.Link.get_establishment_rate">
|
||||||
<span class="sig-name descname"><span class="pre">get_establishment_rate</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_establishment_rate" title="Permalink to this definition">#</a></dt>
|
<span class="sig-name descname"><span class="pre">get_establishment_rate</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_establishment_rate" title="Permalink to this definition">#</a></dt>
|
||||||
@@ -1072,7 +1131,7 @@ thus preserved. This method can be used for authentication.</p>
|
|||||||
<span class="sig-name descname"><span class="pre">no_inbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_inbound_for" title="Permalink to this definition">#</a></dt>
|
<span class="sig-name descname"><span class="pre">no_inbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_inbound_for" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><dl class="field-list simple">
|
<dd><dl class="field-list simple">
|
||||||
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
<dd class="field-odd"><p>The time in seconds since last inbound packet on the link.</p>
|
<dd class="field-odd"><p>The time in seconds since last inbound packet on the link. This includes keepalive packets.</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
@@ -1082,7 +1141,17 @@ thus preserved. This method can be used for authentication.</p>
|
|||||||
<span class="sig-name descname"><span class="pre">no_outbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_outbound_for" title="Permalink to this definition">#</a></dt>
|
<span class="sig-name descname"><span class="pre">no_outbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_outbound_for" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><dl class="field-list simple">
|
<dd><dl class="field-list simple">
|
||||||
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
<dd class="field-odd"><p>The time in seconds since last outbound packet on the link.</p>
|
<dd class="field-odd"><p>The time in seconds since last outbound packet on the link. This includes keepalive packets.</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
|
<dl class="py method">
|
||||||
|
<dt class="sig sig-object py" id="RNS.Link.no_data_for">
|
||||||
|
<span class="sig-name descname"><span class="pre">no_data_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_data_for" title="Permalink to this definition">#</a></dt>
|
||||||
|
<dd><dl class="field-list simple">
|
||||||
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
|
<dd class="field-odd"><p>The time in seconds since payload data traversed the link. This excludes keepalive packets.</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
@@ -1092,7 +1161,7 @@ thus preserved. This method can be used for authentication.</p>
|
|||||||
<span class="sig-name descname"><span class="pre">inactive_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.inactive_for" title="Permalink to this definition">#</a></dt>
|
<span class="sig-name descname"><span class="pre">inactive_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.inactive_for" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><dl class="field-list simple">
|
<dd><dl class="field-list simple">
|
||||||
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
<dt class="field-odd">Returns<span class="colon">:</span></dt>
|
||||||
<dd class="field-odd"><p>The time in seconds since activity on the link.</p>
|
<dd class="field-odd"><p>The time in seconds since activity on the link. This includes keepalive packets.</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
@@ -1536,7 +1605,7 @@ and <code class="docutils literal notranslate"><span class="pre">BufferedRWPair<
|
|||||||
<code class="docutils literal notranslate"><span class="pre">RawChannelReader</span></code> and <code class="docutils literal notranslate"><span class="pre">RawChannelWriter</span></code>.</p>
|
<code class="docutils literal notranslate"><span class="pre">RawChannelReader</span></code> and <code class="docutils literal notranslate"><span class="pre">RawChannelWriter</span></code>.</p>
|
||||||
<dl class="py method">
|
<dl class="py method">
|
||||||
<dt class="sig sig-object py" id="RNS.Buffer.create_reader">
|
<dt class="sig sig-object py" id="RNS.Buffer.create_reader">
|
||||||
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_reader</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">BufferedReader</span></span></span><a class="headerlink" href="#RNS.Buffer.create_reader" title="Permalink to this definition">#</a></dt>
|
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_reader</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">BufferedReader</span></span></span><a class="headerlink" href="#RNS.Buffer.create_reader" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><p>Create a buffered reader that reads binary data sent
|
<dd><p>Create a buffered reader that reads binary data sent
|
||||||
over a <code class="docutils literal notranslate"><span class="pre">Channel</span></code>, with an optional callback when
|
over a <code class="docutils literal notranslate"><span class="pre">Channel</span></code>, with an optional callback when
|
||||||
new data is available.</p>
|
new data is available.</p>
|
||||||
@@ -1581,7 +1650,7 @@ of this object, see the Python documentation for
|
|||||||
|
|
||||||
<dl class="py method">
|
<dl class="py method">
|
||||||
<dt class="sig sig-object py" id="RNS.Buffer.create_bidirectional_buffer">
|
<dt class="sig sig-object py" id="RNS.Buffer.create_bidirectional_buffer">
|
||||||
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_bidirectional_buffer</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">receive_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">send_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">BufferedRWPair</span></span></span><a class="headerlink" href="#RNS.Buffer.create_bidirectional_buffer" title="Permalink to this definition">#</a></dt>
|
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_bidirectional_buffer</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">receive_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">send_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">BufferedRWPair</span></span></span><a class="headerlink" href="#RNS.Buffer.create_bidirectional_buffer" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><p>Create a buffered reader/writer pair that reads and
|
<dd><p>Create a buffered reader/writer pair that reads and
|
||||||
writes binary data over a <code class="docutils literal notranslate"><span class="pre">Channel</span></code>, with an
|
writes binary data over a <code class="docutils literal notranslate"><span class="pre">Channel</span></code>, with an
|
||||||
optional callback when new data is available.</p>
|
optional callback when new data is available.</p>
|
||||||
@@ -1851,6 +1920,8 @@ will announce it.</p>
|
|||||||
<li><a class="reference internal" href="#RNS.Reticulum"><code class="docutils literal notranslate"><span class="pre">Reticulum</span></code></a><ul>
|
<li><a class="reference internal" href="#RNS.Reticulum"><code class="docutils literal notranslate"><span class="pre">Reticulum</span></code></a><ul>
|
||||||
<li><a class="reference internal" href="#RNS.Reticulum.MTU"><code class="docutils literal notranslate"><span class="pre">MTU</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Reticulum.MTU"><code class="docutils literal notranslate"><span class="pre">MTU</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Reticulum.ANNOUNCE_CAP"><code class="docutils literal notranslate"><span class="pre">ANNOUNCE_CAP</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Reticulum.ANNOUNCE_CAP"><code class="docutils literal notranslate"><span class="pre">ANNOUNCE_CAP</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Reticulum.MINIMUM_BITRATE"><code class="docutils literal notranslate"><span class="pre">MINIMUM_BITRATE</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Reticulum.get_instance"><code class="docutils literal notranslate"><span class="pre">get_instance()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Reticulum.should_use_implicit_proof"><code class="docutils literal notranslate"><span class="pre">should_use_implicit_proof()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Reticulum.should_use_implicit_proof"><code class="docutils literal notranslate"><span class="pre">should_use_implicit_proof()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Reticulum.transport_enabled"><code class="docutils literal notranslate"><span class="pre">transport_enabled()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Reticulum.transport_enabled"><code class="docutils literal notranslate"><span class="pre">transport_enabled()</span></code></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1924,9 +1995,14 @@ will announce it.</p>
|
|||||||
<li><a class="reference internal" href="#RNS.Link.STALE_TIME"><code class="docutils literal notranslate"><span class="pre">STALE_TIME</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.STALE_TIME"><code class="docutils literal notranslate"><span class="pre">STALE_TIME</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.identify"><code class="docutils literal notranslate"><span class="pre">identify()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.identify"><code class="docutils literal notranslate"><span class="pre">identify()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.request"><code class="docutils literal notranslate"><span class="pre">request()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.request"><code class="docutils literal notranslate"><span class="pre">request()</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Link.track_phy_stats"><code class="docutils literal notranslate"><span class="pre">track_phy_stats()</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Link.get_rssi"><code class="docutils literal notranslate"><span class="pre">get_rssi()</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Link.get_snr"><code class="docutils literal notranslate"><span class="pre">get_snr()</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Link.get_q"><code class="docutils literal notranslate"><span class="pre">get_q()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.get_establishment_rate"><code class="docutils literal notranslate"><span class="pre">get_establishment_rate()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.get_establishment_rate"><code class="docutils literal notranslate"><span class="pre">get_establishment_rate()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.no_inbound_for"><code class="docutils literal notranslate"><span class="pre">no_inbound_for()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.no_inbound_for"><code class="docutils literal notranslate"><span class="pre">no_inbound_for()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.no_outbound_for"><code class="docutils literal notranslate"><span class="pre">no_outbound_for()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.no_outbound_for"><code class="docutils literal notranslate"><span class="pre">no_outbound_for()</span></code></a></li>
|
||||||
|
<li><a class="reference internal" href="#RNS.Link.no_data_for"><code class="docutils literal notranslate"><span class="pre">no_data_for()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.inactive_for"><code class="docutils literal notranslate"><span class="pre">inactive_for()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.inactive_for"><code class="docutils literal notranslate"><span class="pre">inactive_for()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.get_remote_identity"><code class="docutils literal notranslate"><span class="pre">get_remote_identity()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.get_remote_identity"><code class="docutils literal notranslate"><span class="pre">get_remote_identity()</span></code></a></li>
|
||||||
<li><a class="reference internal" href="#RNS.Link.teardown"><code class="docutils literal notranslate"><span class="pre">teardown()</span></code></a></li>
|
<li><a class="reference internal" href="#RNS.Link.teardown"><code class="docutils literal notranslate"><span class="pre">teardown()</span></code></a></li>
|
||||||
@@ -2013,11 +2089,14 @@ will announce it.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+15
-12
@@ -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-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" />
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.7.4 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -158,13 +158,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -262,12 +262,15 @@
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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
+17
-14
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Support Reticulum - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Support Reticulum - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -330,11 +330,14 @@ 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?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Understanding Reticulum - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Understanding Reticulum - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -286,7 +286,7 @@ it can be easily modified and replicated by anyone interested in doing so.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li><dl class="simple">
|
<li><dl class="simple">
|
||||||
<dt><strong>Very low bandwidth requirements</strong></dt><dd><p>Reticulum should be able to function reliably over links with a transmission capacity as low
|
<dt><strong>Very low bandwidth requirements</strong></dt><dd><p>Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||||
as <em>500 bits per second</em>.</p>
|
as <em>5 bits per second</em>.</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
@@ -831,7 +831,7 @@ note that Reticulum is designed to be usable on more or less any computing devic
|
|||||||
or less any medium that allows you to send and receive data, which satisfies some very low
|
or less any medium that allows you to send and receive data, which satisfies some very low
|
||||||
minimum requirements.</p>
|
minimum requirements.</p>
|
||||||
<p>The communication channel must support at least half-duplex operation, and provide an average
|
<p>The communication channel must support at least half-duplex operation, and provide an average
|
||||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes. The
|
||||||
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
||||||
runtime environment.</p>
|
runtime environment.</p>
|
||||||
<p>That being said, this reference setup has been outlined to provide a common platform for anyone
|
<p>That being said, this reference setup has been outlined to provide a common platform for anyone
|
||||||
@@ -1196,11 +1196,14 @@ those risks are acceptable to you.</p>
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
+61
-15
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>Using Reticulum on Your System - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -886,6 +886,8 @@ assignment varies from one boot to another.</p>
|
|||||||
<span id="using-systemd"></span><h3>Reticulum as a System Service<a class="headerlink" href="#reticulum-as-a-system-service" title="Permalink to this heading">#</a></h3>
|
<span id="using-systemd"></span><h3>Reticulum as a System Service<a class="headerlink" href="#reticulum-as-a-system-service" title="Permalink to this heading">#</a></h3>
|
||||||
<p>Instead of starting Reticulum manually, you can install <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> as a system
|
<p>Instead of starting Reticulum manually, you can install <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> as a system
|
||||||
service and have it start automatically at boot.</p>
|
service and have it start automatically at boot.</p>
|
||||||
|
<section id="systemwide-service">
|
||||||
|
<h4>Systemwide Service<a class="headerlink" href="#systemwide-service" title="Permalink to this heading">#</a></h4>
|
||||||
<p>If you installed Reticulum with <code class="docutils literal notranslate"><span class="pre">pip</span></code>, the <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> program will most likely
|
<p>If you installed Reticulum with <code class="docutils literal notranslate"><span class="pre">pip</span></code>, the <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> program will most likely
|
||||||
be located in a user-local installation path only, which means <code class="docutils literal notranslate"><span class="pre">systemd</span></code> will not
|
be located in a user-local installation path only, which means <code class="docutils literal notranslate"><span class="pre">systemd</span></code> will not
|
||||||
be able to execute it. In this case, you can simply symlink the <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> program
|
be able to execute it. In this case, you can simply symlink the <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> program
|
||||||
@@ -926,6 +928,43 @@ WantedBy=multi-user.target
|
|||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<section id="userspace-service">
|
||||||
|
<h4>Userspace Service<a class="headerlink" href="#userspace-service" title="Permalink to this heading">#</a></h4>
|
||||||
|
<p>Alternatively you can use a user systemd service instead of a system wide one. This way the whole setup can be done as a regular user.
|
||||||
|
Create a user systemd service files <code class="docutils literal notranslate"><span class="pre">~/.config/systemd/user/rnsd.service</span></code> with the following content:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[Unit]
|
||||||
|
Description=Reticulum Network Stack Daemon
|
||||||
|
After=default.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
# If you run Reticulum on WiFi devices,
|
||||||
|
# or other devices that need some extra
|
||||||
|
# time to initialise, you might want to
|
||||||
|
# add a short delay before Reticulum is
|
||||||
|
# started by systemd:
|
||||||
|
# ExecStartPre=/bin/sleep 10
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
ExecStart=RNS_BIN_DIR/rnsd --service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>Replace <code class="docutils literal notranslate"><span class="pre">RNS_BIN_DIR</span></code> with the path to your Reticulum binary directory (eg. /home/USERNAMEHERE/rns/bin).</p>
|
||||||
|
<p>Start user service:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>systemctl --user daemon-reload
|
||||||
|
systemctl --user start rnsd.service
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
<p>If you want to automatically start <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> without having to log in as the USERNAMEHERE, do:</p>
|
||||||
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>sudo loginctl enable-linger USERNAMEHERE
|
||||||
|
systemctl --user enable rnsd.service
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -1000,7 +1039,11 @@ WantedBy=multi-user.target
|
|||||||
</li>
|
</li>
|
||||||
<li><a class="reference internal" href="#improving-system-configuration">Improving System Configuration</a><ul>
|
<li><a class="reference internal" href="#improving-system-configuration">Improving System Configuration</a><ul>
|
||||||
<li><a class="reference internal" href="#fixed-serial-port-names">Fixed Serial Port Names</a></li>
|
<li><a class="reference internal" href="#fixed-serial-port-names">Fixed Serial Port Names</a></li>
|
||||||
<li><a class="reference internal" href="#reticulum-as-a-system-service">Reticulum as a System Service</a></li>
|
<li><a class="reference internal" href="#reticulum-as-a-system-service">Reticulum as a System Service</a><ul>
|
||||||
|
<li><a class="reference internal" href="#systemwide-service">Systemwide Service</a></li>
|
||||||
|
<li><a class="reference internal" href="#userspace-service">Userspace Service</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1014,11 +1057,14 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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
-15
@@ -5,13 +5,13 @@
|
|||||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
<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-7.1.2, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>What is Reticulum? - Reticulum Network Stack 0.6.2 beta documentation</title>
|
<title>What is Reticulum? - Reticulum Network Stack 0.7.4 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.2 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
<aside class="sidebar-drawer">
|
<aside class="sidebar-drawer">
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand centered" 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.6.2 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
@@ -293,7 +293,7 @@ considered complete and stable at the moment, but could change if absolutely war
|
|||||||
<section id="where-can-reticulum-be-used">
|
<section id="where-can-reticulum-be-used">
|
||||||
<h2>Where can Reticulum be Used?<a class="headerlink" href="#where-can-reticulum-be-used" title="Permalink to this heading">#</a></h2>
|
<h2>Where can Reticulum be Used?<a class="headerlink" href="#where-can-reticulum-be-used" title="Permalink to this heading">#</a></h2>
|
||||||
<p>Over practically any medium that can support at least a half-duplex channel
|
<p>Over practically any medium that can support at least a half-duplex channel
|
||||||
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
|
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
|
||||||
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
||||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||||
of the types of interfaces Reticulum was designed for.</p>
|
of the types of interfaces Reticulum was designed for.</p>
|
||||||
@@ -434,11 +434,14 @@ 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?v=2c9eb04b"></script>
|
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
<script src="_static/doctools.js?v=888ff710"></script>
|
<script src="_static/jquery.js"></script>
|
||||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
<script src="_static/underscore.js"></script>
|
||||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
|
||||||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
<script src="_static/doctools.js"></script>
|
||||||
<script src="_static/copybutton.js?v=f281be69"></script>
|
<script src="_static/sphinx_highlight.js"></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>
|
||||||
@@ -415,7 +415,7 @@ locally on your device using the following command:
|
|||||||
|
|
||||||
It is also possible to include Reticulum in apps compiled and distributed as
|
It is also possible to include Reticulum in apps compiled and distributed as
|
||||||
Android APKs. A detailed tutorial and example source code will be included
|
Android APKs. A detailed tutorial and example source code will be included
|
||||||
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and startig point.
|
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and starting point.
|
||||||
|
|
||||||
|
|
||||||
ARM64
|
ARM64
|
||||||
|
|||||||
@@ -33,9 +33,20 @@ system, which should be enabled by default in almost all OSes.
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
# This example demonstrates a TCP server interface.
|
# This example demonstrates a bare-minimum setup
|
||||||
# It will listen for incoming connections on the
|
# of an Auto Interface. It will allow communica-
|
||||||
# specified IP address and port number.
|
# tion with all other reachable devices on all
|
||||||
|
# usable physical ethernet-based devices that
|
||||||
|
# are available on the system.
|
||||||
|
|
||||||
|
[[Default Interface]]
|
||||||
|
type = AutoInterface
|
||||||
|
interface_enabled = True
|
||||||
|
|
||||||
|
# This example demonstrates an more specifically
|
||||||
|
# configured Auto Interface, that only uses spe-
|
||||||
|
# cific physical interfaces, and has a number of
|
||||||
|
# other configuration options set.
|
||||||
|
|
||||||
[[Default Interface]]
|
[[Default Interface]]
|
||||||
type = AutoInterface
|
type = AutoInterface
|
||||||
@@ -47,6 +58,12 @@ system, which should be enabled by default in almost all OSes.
|
|||||||
|
|
||||||
group_id = reticulum
|
group_id = reticulum
|
||||||
|
|
||||||
|
# You can also choose the multicast address type:
|
||||||
|
# temporary (default, Temporary Multicast Address)
|
||||||
|
# or permanent (Permanent Multicast Address)
|
||||||
|
|
||||||
|
multicast_address_type = permanent
|
||||||
|
|
||||||
# You can also select specifically which
|
# You can also select specifically which
|
||||||
# kernel networking devices to use.
|
# kernel networking devices to use.
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ guide the design of Reticulum:
|
|||||||
it can be easily modified and replicated by anyone interested in doing so.
|
it can be easily modified and replicated by anyone interested in doing so.
|
||||||
* **Very low bandwidth requirements**
|
* **Very low bandwidth requirements**
|
||||||
Reticulum should be able to function reliably over links with a transmission capacity as low
|
Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||||
as *500 bits per second*.
|
as *5 bits per second*.
|
||||||
* **Encryption by default**
|
* **Encryption by default**
|
||||||
Reticulum must use strong encryption by default for all communication.
|
Reticulum must use strong encryption by default for all communication.
|
||||||
* **Initiator Anonymity**
|
* **Initiator Anonymity**
|
||||||
@@ -595,7 +595,7 @@ or less any medium that allows you to send and receive data, which satisfies som
|
|||||||
minimum requirements.
|
minimum requirements.
|
||||||
|
|
||||||
The communication channel must support at least half-duplex operation, and provide an average
|
The communication channel must support at least half-duplex operation, and provide an average
|
||||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes. The
|
||||||
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
|
||||||
runtime environment.
|
runtime environment.
|
||||||
|
|
||||||
|
|||||||
@@ -780,6 +780,9 @@ Reticulum as a System Service
|
|||||||
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
|
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
|
||||||
service and have it start automatically at boot.
|
service and have it start automatically at boot.
|
||||||
|
|
||||||
|
Systemwide Service
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
If you installed Reticulum with ``pip``, the ``rnsd`` program will most likely
|
If you installed Reticulum with ``pip``, the ``rnsd`` program will most likely
|
||||||
be located in a user-local installation path only, which means ``systemd`` will not
|
be located in a user-local installation path only, which means ``systemd`` will not
|
||||||
be able to execute it. In this case, you can simply symlink the ``rnsd`` program
|
be able to execute it. In this case, you can simply symlink the ``rnsd`` program
|
||||||
@@ -827,3 +830,48 @@ If you want to automatically start ``rnsd`` at boot, run:
|
|||||||
.. code:: text
|
.. code:: text
|
||||||
|
|
||||||
sudo systemctl enable rnsd
|
sudo systemctl enable rnsd
|
||||||
|
|
||||||
|
Userspace Service
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Alternatively you can use a user systemd service instead of a system wide one. This way the whole setup can be done as a regular user.
|
||||||
|
Create a user systemd service files ``~/.config/systemd/user/rnsd.service`` with the following content:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Reticulum Network Stack Daemon
|
||||||
|
After=default.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
# If you run Reticulum on WiFi devices,
|
||||||
|
# or other devices that need some extra
|
||||||
|
# time to initialise, you might want to
|
||||||
|
# add a short delay before Reticulum is
|
||||||
|
# started by systemd:
|
||||||
|
# ExecStartPre=/bin/sleep 10
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
ExecStart=RNS_BIN_DIR/rnsd --service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
||||||
|
Replace ``RNS_BIN_DIR`` with the path to your Reticulum binary directory (eg. /home/USERNAMEHERE/rns/bin).
|
||||||
|
|
||||||
|
Start user service:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user start rnsd.service
|
||||||
|
|
||||||
|
If you want to automatically start ``rnsd`` without having to log in as the USERNAMEHERE, do:
|
||||||
|
|
||||||
|
.. code:: text
|
||||||
|
|
||||||
|
sudo loginctl enable-linger USERNAMEHERE
|
||||||
|
systemctl --user enable rnsd.service
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ What does Reticulum Offer?
|
|||||||
Where can Reticulum be Used?
|
Where can Reticulum be Used?
|
||||||
============================
|
============================
|
||||||
Over practically any medium that can support at least a half-duplex channel
|
Over practically any medium that can support at least a half-duplex channel
|
||||||
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
|
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
|
||||||
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
|
||||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||||
of the types of interfaces Reticulum was designed for.
|
of the types of interfaces Reticulum was designed for.
|
||||||
|
|||||||
+44
-4
@@ -99,6 +99,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
@@ -120,6 +123,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
@@ -140,6 +146,7 @@ class TestLink(unittest.TestCase):
|
|||||||
pstart = time.time()
|
pstart = time.time()
|
||||||
print("Sending "+str(num_packets)+" link packets of "+str(packet_size)+" bytes...")
|
print("Sending "+str(num_packets)+" link packets of "+str(packet_size)+" bytes...")
|
||||||
for i in range(0, num_packets):
|
for i in range(0, num_packets):
|
||||||
|
time.sleep(0.003)
|
||||||
b += packet_size
|
b += packet_size
|
||||||
data = os.urandom(packet_size)
|
data = os.urandom(packet_size)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
@@ -188,6 +195,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
@@ -203,6 +213,7 @@ class TestLink(unittest.TestCase):
|
|||||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
|
# This is a hack, don't do it. Use the callbacks instead.
|
||||||
while resource.status < RNS.Resource.COMPLETE:
|
while resource.status < RNS.Resource.COMPLETE:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
@@ -224,6 +235,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
@@ -239,6 +253,7 @@ class TestLink(unittest.TestCase):
|
|||||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
|
# This is a hack, don't do it. Use the callbacks instead.
|
||||||
while resource.status < RNS.Resource.COMPLETE:
|
while resource.status < RNS.Resource.COMPLETE:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
@@ -260,6 +275,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
@@ -274,6 +292,7 @@ class TestLink(unittest.TestCase):
|
|||||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
|
# This is a hack, don't do it. Use the callbacks instead.
|
||||||
while resource.status < RNS.Resource.COMPLETE:
|
while resource.status < RNS.Resource.COMPLETE:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
@@ -300,6 +319,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
@@ -314,6 +336,7 @@ class TestLink(unittest.TestCase):
|
|||||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
|
# This is a hack, don't do it. Use the callbacks instead.
|
||||||
while resource.status < RNS.Resource.COMPLETE:
|
while resource.status < RNS.Resource.COMPLETE:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
@@ -325,6 +348,10 @@ class TestLink(unittest.TestCase):
|
|||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.CLOSED)
|
self.assertEqual(l1.status, RNS.Link.CLOSED)
|
||||||
|
|
||||||
|
large_resource_status = None
|
||||||
|
def lr_callback(self, resource):
|
||||||
|
TestLink.large_resource_status = resource.status
|
||||||
|
|
||||||
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
|
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
|
||||||
def test_9_large_resource(self):
|
def test_9_large_resource(self):
|
||||||
if RNS.Cryptography.backend() == "internal":
|
if RNS.Cryptography.backend() == "internal":
|
||||||
@@ -339,6 +366,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
@@ -347,17 +377,18 @@ class TestLink(unittest.TestCase):
|
|||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
|
||||||
resource_timeout = 120
|
resource_timeout = 120
|
||||||
resource_size = 35*1000*1000
|
resource_size = 50*1000*1000
|
||||||
data = os.urandom(resource_size)
|
data = os.urandom(resource_size)
|
||||||
print("Sending "+self.size_str(resource_size)+" resource...")
|
print("Sending "+self.size_str(resource_size)+" resource...")
|
||||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
while resource.status < RNS.Resource.COMPLETE:
|
TestLink.large_resource_status = resource.status
|
||||||
|
while TestLink.large_resource_status < RNS.Resource.COMPLETE:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
t = time.time() - start
|
t = time.time() - start
|
||||||
self.assertEqual(resource.status, RNS.Resource.COMPLETE)
|
self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE)
|
||||||
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps")
|
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps")
|
||||||
|
|
||||||
l1.teardown()
|
l1.teardown()
|
||||||
@@ -375,6 +406,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
@@ -424,6 +458,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
@@ -477,6 +514,9 @@ class TestLink(unittest.TestCase):
|
|||||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||||
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
|
||||||
|
|
||||||
|
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|||||||
Reference in New Issue
Block a user