mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-06-22 20:12:37 -07:00
Compare commits
193 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| e0da489156 | |||
| 2dcf1350e7 | |||
| 1e280611ce | |||
| f1d107846f | |||
| cc951dcb53 | |||
| b5856a3706 | |||
| ed3479da9a | |||
| 5e15f421b7 | |||
| 0a9366ba6e | |||
| cf31435f39 | |||
| 9f58860842 | |||
| 875348383d | |||
| f79f190525 | |||
| 5e27a81412 | |||
| 0dcb009579 | |||
| 943f76804b | |||
| 8bbe6ae3ae | |||
| f0d85dd078 | |||
| f85dda1829 | |||
| 91e064cdf1 | |||
| fb4e53f6e3 | |||
| 03340ed091 | |||
| ed424fa0a2 | |||
| 406ab216d1 | |||
| 00d8a2064d | |||
| 38b920e393 | |||
| 1ed000c4d9 | |||
| d360958d10 | |||
| fcdb455d73 | |||
| 575639b721 | |||
| 492573f9fe | |||
| c5d30f8ee6 | |||
| 3c4791a622 | |||
| 803a5736c9 | |||
| 267ffbdf5f | |||
| 52028aa44c | |||
| c5248d53d6 | |||
| 2d2f0947ac | |||
| 4fa616a326 | |||
| 136713eec1 | |||
| 0fd75cb819 | |||
| ea52153969 | |||
| 3854781028 | |||
| ec2805f357 | |||
| b5cb3a65dd | |||
| c79cb3aa20 | |||
| 8bff119691 | |||
| 5e0b2c5b42 | |||
| 8908022b88 | |||
| b0dda0ed86 | |||
| 6ae72d4225 | |||
| 0a188a2d39 | |||
| 036abb28fe | |||
| a732767a28 | |||
| 32a1261d98 | |||
| 27c5af3bbc | |||
| 5872108da3 |
@@ -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.
|
||||
+214
-2
@@ -1,4 +1,216 @@
|
||||
### 2023-09-21: RNS β 0.6.0
|
||||
### 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
|
||||
|
||||
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**
|
||||
- Added configuration option to specify shared instance RPC key
|
||||
- Reordered airtime stats in `rnstatus`
|
||||
- Updated log levels on Android
|
||||
|
||||
**Bugfixes**
|
||||
- Adding missing superclass init on Android interfaces
|
||||
|
||||
**Release Hashes**
|
||||
```
|
||||
a9958ad90f34f344003e18077f7abd3fa85666a39dc0cae8580071820dee13f9 rns-0.6.2-py3-none-any.whl
|
||||
e68e8837d35d1a07a82c4b0e9db50ceace737a650e6e7e9ce2d9a013fd28f529 rnspure-0.6.2-py3-none-any.whl
|
||||
```
|
||||
|
||||
### 2023-10-01: RNS β 0.6.1
|
||||
|
||||
This release brings a number of bugfixes, along with useful new mechanisms for ensuring network stability under high, non-constructive and unusual announce load situation.
|
||||
|
||||
**Changes**
|
||||
- Added announce ingress rate control for new and unknown destinations
|
||||
- Added per-interface announce frequency monitoring to the transport engine
|
||||
- Added per-interface announce burst hold queues
|
||||
- Added announce frequency statistics to `rnstatus`
|
||||
- Added option to sort `rnstatus` output according to various metrics
|
||||
- Added timeout options to `rnprobe`
|
||||
- Added ability to drop all paths via a specific transport instance to `rnpath`
|
||||
- Added new options and features to documentation and manual
|
||||
|
||||
**Bugfixes**
|
||||
- Fixed announce queue not clearing all announces with exceeded retry limit at the same time
|
||||
- Fixed a bug that caused local packet RSSI and SNR cache to get stuck
|
||||
- Fixed output formatting in `rncp`
|
||||
- Fixed `rnid` not allowing single-aspect destination names
|
||||
- Fixed a number of typos in the documentation
|
||||
|
||||
**Release Hashes**
|
||||
```
|
||||
461e5cafa7560dcd3ec047141d10f0f48f151c36e1af1d65ec6c65f732cea46a rns-0.6.1-py3-none-any.whl
|
||||
be6a4a6069f2d050e21582f2cf9d3bb59ed4040a0f07761a540bd752d90ea591 rnspure-0.6.1-py3-none-any.whl
|
||||
```
|
||||
|
||||
#### 2023-09-21: RNS β 0.6.0
|
||||
|
||||
This release brings a few performance improvements, additions to the included utilities, and fixes a number of bugs.
|
||||
|
||||
@@ -898,4 +1110,4 @@ This was the first publicly available pre-release alpha of Reticulum.
|
||||
|
||||
### 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.
|
||||
|
||||
+14
-6
@@ -2,21 +2,29 @@
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ def program_setup(configpath):
|
||||
# Destinations are endpoints in Reticulum, that can be addressed
|
||||
# and communicated with. Destinations can also announce their
|
||||
# existence, which will let the network know they are reachable
|
||||
# and autoomatically create paths to them, from anywhere else
|
||||
# and automatically create paths to them, from anywhere else
|
||||
# in the network.
|
||||
destination_1 = RNS.Destination(
|
||||
identity,
|
||||
@@ -53,7 +53,7 @@ def program_setup(configpath):
|
||||
)
|
||||
|
||||
# We configure the destinations to automatically prove all
|
||||
# packets adressed to it. By doing this, RNS will automatically
|
||||
# packets addressed to it. By doing this, RNS will automatically
|
||||
# generate a proof for each incoming packet and transmit it
|
||||
# back to the sender of that packet. This will let anyone that
|
||||
# tries to communicate with the destination know whether their
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ def server(configpath):
|
||||
)
|
||||
|
||||
# We configure the destination to automatically prove all
|
||||
# packets adressed to it. By doing this, RNS will automatically
|
||||
# packets addressed to it. By doing this, RNS will automatically
|
||||
# generate a proof for each incoming packet and transmit it
|
||||
# back to the sender of that packet.
|
||||
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||
|
||||
@@ -449,8 +449,7 @@ def link_established(link):
|
||||
# And set up a small job to check for
|
||||
# a potential timeout in receiving the
|
||||
# file list
|
||||
thread = threading.Thread(target=filelist_timeout_job)
|
||||
thread.setDaemon(True)
|
||||
thread = threading.Thread(target=filelist_timeout_job, daemon=True)
|
||||
thread.start()
|
||||
|
||||
# This job just sleeps for the specified
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ def program_setup(configpath):
|
||||
# Destinations are endpoints in Reticulum, that can be addressed
|
||||
# and communicated with. Destinations can also announce their
|
||||
# existence, which will let the network know they are reachable
|
||||
# and autoomatically create paths to them, from anywhere else
|
||||
# and automatically create paths to them, from anywhere else
|
||||
# in the network.
|
||||
destination = RNS.Destination(
|
||||
identity,
|
||||
@@ -36,7 +36,7 @@ def program_setup(configpath):
|
||||
)
|
||||
|
||||
# We configure the destination to automatically prove all
|
||||
# packets adressed to it. By doing this, RNS will automatically
|
||||
# packets addressed to it. By doing this, RNS will automatically
|
||||
# generate a proof for each incoming packet and transmit it
|
||||
# back to the sender of that packet. This will let anyone that
|
||||
# tries to communicate with the destination know whether their
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ko_fi: markqvist
|
||||
custom: "https://unsigned.io/donate"
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License, unless otherwise noted
|
||||
|
||||
Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
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
|
||||
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
|
||||
- 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
|
||||
- AES-128 in CBC mode with PKCS7 padding
|
||||
- HMAC using SHA256 for authentication
|
||||
@@ -80,7 +80,7 @@ following resources.
|
||||
|
||||
## Where can Reticulum be used?
|
||||
Over practically any medium that can support at least a half-duplex channel
|
||||
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
|
||||
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,
|
||||
WiFi and Ethernet devices, free-space optical links, and similar systems are
|
||||
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
|
||||
per second on normal hardware.
|
||||
|
||||
Currently, the usable performance envelope is approximately 500 bits per second
|
||||
to 20 megabits per second, with physical mediums faster than that not being
|
||||
Currently, the usable performance envelope is approximately 150 bits per second
|
||||
to 40 megabits per second, with physical mediums faster than that not being
|
||||
saturated. Performance beyond the current level is intended for future
|
||||
upgrades, but not highly prioritised at this point in time.
|
||||
|
||||
@@ -273,16 +273,16 @@ The testnet also contains a number of [Nomad Network](https://github.com/markqvi
|
||||
You can help support the continued development of open, free and private communications systems by donating via one of the following channels:
|
||||
|
||||
- Monero:
|
||||
```
|
||||
```
|
||||
84FpY1QbxHcgdseePYNmhTHcrgMX4nFfBYtz2GKYToqHVVhJp8Eaw1Z1EedRnKD19b3B8NiLCGVxzKV17UMmmeEsCrPyA5w
|
||||
```
|
||||
- Ethereum
|
||||
```
|
||||
0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a
|
||||
0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73
|
||||
```
|
||||
- Bitcoin
|
||||
```
|
||||
3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq
|
||||
35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH
|
||||
```
|
||||
- Ko-Fi: https://ko-fi.com/markqvist
|
||||
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from __future__ import annotations
|
||||
import bz2
|
||||
import sys
|
||||
|
||||
@@ -45,10 +45,10 @@ class Fernet():
|
||||
|
||||
def __init__(self, key = None):
|
||||
if key == None:
|
||||
raise ValueError("Fernet key cannot be None")
|
||||
raise ValueError("Token key cannot be None")
|
||||
|
||||
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._encryption_key = key[16:]
|
||||
@@ -72,7 +72,7 @@ class Fernet():
|
||||
current_time = int(time.time())
|
||||
|
||||
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(
|
||||
plaintext = PKCS7.pad(data),
|
||||
@@ -87,10 +87,10 @@ class Fernet():
|
||||
|
||||
def decrypt(self, token = None):
|
||||
if not isinstance(token, bytes):
|
||||
raise TypeError("Fernet token must be bytes")
|
||||
raise TypeError("Token must be bytes")
|
||||
|
||||
if not self.verify_hmac(token):
|
||||
raise ValueError("Fernet token HMAC was invalid")
|
||||
raise ValueError("Token HMAC was invalid")
|
||||
|
||||
iv = token[:16]
|
||||
ciphertext = token[16:-32]
|
||||
@@ -107,4 +107,4 @@ class Fernet():
|
||||
return plaintext
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError("Could not decrypt Fernet token")
|
||||
raise ValueError("Could not decrypt token")
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+15
-6
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -145,9 +145,12 @@ class Identity:
|
||||
except:
|
||||
pass
|
||||
|
||||
for destination_hash in storage_known_destinations:
|
||||
if not destination_hash in Identity.known_destinations:
|
||||
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
|
||||
try:
|
||||
for destination_hash in storage_known_destinations:
|
||||
if not destination_hash in Identity.known_destinations:
|
||||
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)
|
||||
file = open(RNS.Reticulum.storagepath+"/known_destinations","wb")
|
||||
@@ -164,6 +167,7 @@ class Identity:
|
||||
|
||||
except Exception as e:
|
||||
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
|
||||
|
||||
@@ -181,7 +185,8 @@ class Identity:
|
||||
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)
|
||||
except:
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
|
||||
else:
|
||||
RNS.log("Destinations file does not exist, no known destinations loaded", RNS.LOG_VERBOSE)
|
||||
@@ -217,7 +222,7 @@ class Identity:
|
||||
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
|
||||
|
||||
@staticmethod
|
||||
def validate_announce(packet):
|
||||
def validate_announce(packet, only_validate_signature=False):
|
||||
try:
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
destination_hash = packet.destination_hash
|
||||
@@ -238,6 +243,10 @@ class Identity:
|
||||
announced_identity.load_public_key(public_key)
|
||||
|
||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||
if only_validate_signature:
|
||||
del announced_identity
|
||||
return True
|
||||
|
||||
hash_material = name_hash+announced_identity.hash
|
||||
expected_hash = RNS.Identity.full_hash(hash_material)[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
||||
|
||||
|
||||
@@ -77,8 +77,7 @@ class AX25KISSInterface(Interface):
|
||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||
RNS.panic()
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
@@ -367,5 +366,8 @@ class AX25KISSInterface(Interface):
|
||||
|
||||
RNS.log("Reconnected serial port for "+str(self))
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "AX25KISSInterface["+self.name+"]"
|
||||
@@ -82,8 +82,7 @@ class KISSInterface(Interface):
|
||||
else:
|
||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
@@ -403,5 +402,8 @@ class KISSInterface(Interface):
|
||||
|
||||
RNS.log("Reconnected serial port for "+str(self))
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "KISSInterface["+self.name+"]"
|
||||
@@ -59,6 +59,7 @@ class KISS():
|
||||
CMD_FB_EXT = 0x41
|
||||
CMD_FB_READ = 0x42
|
||||
CMD_FB_WRITE = 0x43
|
||||
CMD_BT_CTRL = 0x46
|
||||
CMD_PLATFORM = 0x48
|
||||
CMD_MCU = 0x49
|
||||
CMD_FW_VERSION = 0x50
|
||||
@@ -114,7 +115,7 @@ class AndroidBluetoothManager():
|
||||
if self.bt_enabled():
|
||||
return self.bt_adapter.getDefaultAdapter().getBondedDevices()
|
||||
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 []
|
||||
|
||||
def get_potential_devices(self):
|
||||
@@ -166,8 +167,8 @@ class AndroidBluetoothManager():
|
||||
raise IOError("The Bluetooth RFcomm socket could not be connected: "+str(e))
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
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_EXTREME)
|
||||
|
||||
def close(self):
|
||||
if self.connected:
|
||||
@@ -232,6 +233,10 @@ class RNodeInterface(Interface):
|
||||
RECONNECT_WAIT = 5
|
||||
PORT_IO_TIMEOUT = 3
|
||||
|
||||
Q_SNR_MIN_BASE = -9
|
||||
Q_SNR_MAX = 6
|
||||
Q_SNR_STEP = 2
|
||||
|
||||
@classmethod
|
||||
def bluetooth_control(device_serial = None, port = None, enable_bluetooth = False, disable_bluetooth = False, pairing_mode = False):
|
||||
if (port != None or device_serial != None) and (enable_bluetooth or disable_bluetooth or pairing_mode):
|
||||
@@ -349,8 +354,7 @@ class RNodeInterface(Interface):
|
||||
else:
|
||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 508
|
||||
|
||||
@@ -598,8 +602,11 @@ class RNodeInterface(Interface):
|
||||
raise IOError("Invalid device firmware")
|
||||
|
||||
if self.serial != None and self.port != None:
|
||||
self.timeout = 200
|
||||
RNS.log("Serial port "+self.port+" is now open")
|
||||
|
||||
if self.bt_manager != None and self.bt_manager.connected:
|
||||
self.timeout = 1500
|
||||
RNS.log("Bluetooth connection to RNode now open")
|
||||
|
||||
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
|
||||
@@ -861,9 +868,6 @@ class RNodeInterface(Interface):
|
||||
self.owner.inbound(data, self)
|
||||
threading.Thread(target=af, daemon=True).start()
|
||||
|
||||
self.r_stat_rssi = None
|
||||
self.r_stat_snr = None
|
||||
|
||||
|
||||
def processOutgoing(self,data):
|
||||
datalen = len(data)
|
||||
@@ -1045,6 +1049,19 @@ class RNodeInterface(Interface):
|
||||
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
||||
elif (command == KISS.CMD_STAT_SNR):
|
||||
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):
|
||||
if (byte == KISS.FESC):
|
||||
escape = True
|
||||
@@ -1156,7 +1173,7 @@ class RNodeInterface(Interface):
|
||||
if got == 0:
|
||||
time_since_last = int(time.time()*1000) - last_read_ms
|
||||
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""
|
||||
in_frame = False
|
||||
command = KISS.CMD_UNKNOWN
|
||||
@@ -1234,6 +1251,9 @@ class RNodeInterface(Interface):
|
||||
self.setRadioState(KISS.RADIO_STATE_OFF)
|
||||
self.leave()
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "RNodeInterface["+str(self.name)+"]"
|
||||
|
||||
|
||||
@@ -72,8 +72,7 @@ class SerialInterface(Interface):
|
||||
else:
|
||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
@@ -254,5 +253,8 @@ class SerialInterface(Interface):
|
||||
|
||||
RNS.log("Reconnected serial port for "+str(self))
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "SerialInterface["+self.name+"]"
|
||||
|
||||
@@ -76,11 +76,9 @@ class AutoInterface(Interface):
|
||||
|
||||
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
|
||||
from RNS.vendor.ifaddr import niwrapper
|
||||
super().__init__()
|
||||
self.netinfo = niwrapper
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.IN = True
|
||||
@@ -336,6 +334,8 @@ class AutoInterface(Interface):
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
self.carrier_changed = True
|
||||
|
||||
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)
|
||||
|
||||
@@ -433,6 +433,11 @@ class AutoInterface(Interface):
|
||||
self.txb += len(data)
|
||||
|
||||
|
||||
# Until per-device sub-interfacing is implemented,
|
||||
# ingress limiting should be disabled on AutoInterface
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "AutoInterface["+self.name+"]"
|
||||
|
||||
|
||||
@@ -390,8 +390,7 @@ class I2PInterfacePeer(Interface):
|
||||
TUNNEL_STATE_STALE = 0x02
|
||||
|
||||
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
@@ -832,8 +831,7 @@ class I2PInterface(Interface):
|
||||
BITRATE_GUESS = 256*1000
|
||||
|
||||
def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
@@ -964,6 +962,12 @@ class I2PInterface(Interface):
|
||||
def processOutgoing(self, data):
|
||||
pass
|
||||
|
||||
def received_announce(self, from_spawned=False):
|
||||
if from_spawned: self.ia_freq_deque.append(time.time())
|
||||
|
||||
def sent_announce(self, from_spawned=False):
|
||||
if from_spawned: self.oa_freq_deque.append(time.time())
|
||||
|
||||
def detach(self):
|
||||
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
|
||||
self.i2p.stop()
|
||||
|
||||
+148
-3
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,6 +23,7 @@
|
||||
import RNS
|
||||
import time
|
||||
import threading
|
||||
from collections import deque
|
||||
|
||||
class Interface:
|
||||
IN = False
|
||||
@@ -39,18 +40,161 @@ class Interface:
|
||||
MODE_BOUNDARY = 0x05
|
||||
MODE_GATEWAY = 0x06
|
||||
|
||||
# Which interface modes a Transport Node
|
||||
# should actively discover paths for.
|
||||
# Which interface modes a Transport Node should
|
||||
# actively discover paths for.
|
||||
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
||||
|
||||
# How many samples to use for announce
|
||||
# frequency calculations
|
||||
IA_FREQ_SAMPLES = 6
|
||||
OA_FREQ_SAMPLES = 6
|
||||
|
||||
# Maximum amount of ingress limited announces
|
||||
# to hold at any given time.
|
||||
MAX_HELD_ANNOUNCES = 256
|
||||
|
||||
# How long a spawned interface will be
|
||||
# considered to be newly created. Two
|
||||
# hours by default.
|
||||
IC_NEW_TIME = 2*60*60
|
||||
IC_BURST_FREQ_NEW = 3.5
|
||||
IC_BURST_FREQ = 12
|
||||
IC_BURST_HOLD = 1*60
|
||||
IC_BURST_PENALTY = 5*60
|
||||
IC_HELD_RELEASE_INTERVAL = 30
|
||||
|
||||
def __init__(self):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
self.created = time.time()
|
||||
self.online = False
|
||||
self.bitrate = 1e6
|
||||
|
||||
self.ingress_control = True
|
||||
self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES
|
||||
self.ic_burst_hold = Interface.IC_BURST_HOLD
|
||||
self.ic_burst_active = False
|
||||
self.ic_burst_activated = 0
|
||||
self.ic_held_release = 0
|
||||
self.ic_burst_freq_new = Interface.IC_BURST_FREQ_NEW
|
||||
self.ic_burst_freq = Interface.IC_BURST_FREQ
|
||||
self.ic_new_time = Interface.IC_NEW_TIME
|
||||
self.ic_burst_penalty = Interface.IC_BURST_PENALTY
|
||||
self.ic_held_release_interval = Interface.IC_HELD_RELEASE_INTERVAL
|
||||
self.held_announces = {}
|
||||
|
||||
self.ia_freq_deque = deque(maxlen=Interface.IA_FREQ_SAMPLES)
|
||||
self.oa_freq_deque = deque(maxlen=Interface.OA_FREQ_SAMPLES)
|
||||
|
||||
def get_hash(self):
|
||||
return RNS.Identity.full_hash(str(self).encode("utf-8"))
|
||||
|
||||
# This is a generic function for determining when an interface
|
||||
# should activate ingress limiting. Since this can vary for
|
||||
# different interface types, this function should be overwritten
|
||||
# in case a particular interface requires a different approach.
|
||||
def should_ingress_limit(self):
|
||||
if self.ingress_control:
|
||||
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
|
||||
ia_freq = self.incoming_announce_frequency()
|
||||
|
||||
if self.ic_burst_active:
|
||||
if ia_freq < freq_threshold and time.time() > self.ic_burst_activated+self.ic_burst_hold:
|
||||
self.ic_burst_active = False
|
||||
self.ic_held_release = time.time() + self.ic_burst_penalty
|
||||
return True
|
||||
|
||||
else:
|
||||
if ia_freq > freq_threshold:
|
||||
self.ic_burst_active = True
|
||||
self.ic_burst_activated = time.time()
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def age(self):
|
||||
return time.time()-self.created
|
||||
|
||||
def hold_announce(self, announce_packet):
|
||||
if announce_packet.destination_hash in self.held_announces:
|
||||
self.held_announces[announce_packet.destination_hash] = announce_packet
|
||||
elif not len(self.held_announces) >= self.ic_max_held_announces:
|
||||
self.held_announces[announce_packet.destination_hash] = announce_packet
|
||||
|
||||
def process_held_announces(self):
|
||||
try:
|
||||
if not self.should_ingress_limit() and len(self.held_announces) > 0 and time.time() > self.ic_held_release:
|
||||
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
|
||||
ia_freq = self.incoming_announce_frequency()
|
||||
if ia_freq < freq_threshold:
|
||||
selected_announce_packet = None
|
||||
min_hops = RNS.Transport.PATHFINDER_M
|
||||
for destination_hash in self.held_announces:
|
||||
announce_packet = self.held_announces[destination_hash]
|
||||
if announce_packet.hops < min_hops:
|
||||
min_hops = announce_packet.hops
|
||||
selected_announce_packet = announce_packet
|
||||
|
||||
if selected_announce_packet != None:
|
||||
RNS.log("Releasing held announce packet "+str(selected_announce_packet)+" from "+str(self), RNS.LOG_EXTREME)
|
||||
self.ic_held_release = time.time() + self.ic_held_release_interval
|
||||
self.held_announces.pop(selected_announce_packet.destination_hash)
|
||||
def release():
|
||||
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
|
||||
threading.Thread(target=release, daemon=True).start()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while processing held announces for "+str(self), RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
def received_announce(self):
|
||||
self.ia_freq_deque.append(time.time())
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||
self.parent_interface.received_announce(from_spawned=True)
|
||||
|
||||
def sent_announce(self):
|
||||
self.oa_freq_deque.append(time.time())
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||
self.parent_interface.sent_announce(from_spawned=True)
|
||||
|
||||
def incoming_announce_frequency(self):
|
||||
if not len(self.ia_freq_deque) > 1:
|
||||
return 0
|
||||
else:
|
||||
dq_len = len(self.ia_freq_deque)
|
||||
delta_sum = 0
|
||||
for i in range(1,dq_len):
|
||||
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
|
||||
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
|
||||
|
||||
if delta_sum == 0:
|
||||
avg = 0
|
||||
else:
|
||||
avg = 1/(delta_sum/(dq_len))
|
||||
|
||||
return avg
|
||||
|
||||
def outgoing_announce_frequency(self):
|
||||
if not len(self.oa_freq_deque) > 1:
|
||||
return 0
|
||||
else:
|
||||
dq_len = len(self.oa_freq_deque)
|
||||
delta_sum = 0
|
||||
for i in range(1,dq_len):
|
||||
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
|
||||
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
|
||||
|
||||
if delta_sum == 0:
|
||||
avg = 0
|
||||
else:
|
||||
avg = 1/(delta_sum/(dq_len))
|
||||
|
||||
return avg
|
||||
|
||||
def process_announce_queue(self):
|
||||
if not hasattr(self, "announce_cap"):
|
||||
self.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
|
||||
@@ -79,6 +223,7 @@ class Interface:
|
||||
self.announce_allowed_at = now + wait_time
|
||||
|
||||
self.processOutgoing(selected["raw"])
|
||||
self.sent_announce()
|
||||
|
||||
if selected in self.announce_queue:
|
||||
self.announce_queue.remove(selected)
|
||||
|
||||
@@ -70,8 +70,7 @@ class KISSInterface(Interface):
|
||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||
RNS.panic()
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
@@ -349,5 +348,8 @@ class KISSInterface(Interface):
|
||||
|
||||
RNS.log("Reconnected serial port for "+str(self))
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "KISSInterface["+self.name+"]"
|
||||
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,6 +28,7 @@ import time
|
||||
import sys
|
||||
import os
|
||||
import RNS
|
||||
from threading import Lock
|
||||
|
||||
class HDLC():
|
||||
FLAG = 0x7E
|
||||
@@ -50,11 +51,10 @@ class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
self.server_address = self.socket.getsockname()
|
||||
|
||||
class LocalClientInterface(Interface):
|
||||
RECONNECT_WAIT = 3
|
||||
RECONNECT_WAIT = 8
|
||||
|
||||
def __init__(self, owner, name, target_port = None, connected_socket=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
# TODO: Remove at some point
|
||||
# self.rxptime = 0
|
||||
@@ -78,6 +78,7 @@ class LocalClientInterface(Interface):
|
||||
self.target_ip = None
|
||||
self.target_port = None
|
||||
self.socket = connected_socket
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
|
||||
self.is_connected_to_shared_instance = False
|
||||
|
||||
@@ -103,8 +104,12 @@ class LocalClientInterface(Interface):
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def connect(self):
|
||||
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.online = True
|
||||
@@ -137,7 +142,10 @@ class LocalClientInterface(Interface):
|
||||
thread = threading.Thread(target=self.read_loop)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
RNS.Transport.shared_connection_reappeared()
|
||||
def job():
|
||||
time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
|
||||
RNS.Transport.shared_connection_reappeared()
|
||||
threading.Thread(target=job, daemon=True).start()
|
||||
|
||||
else:
|
||||
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
|
||||
@@ -145,9 +153,6 @@ class LocalClientInterface(Interface):
|
||||
|
||||
|
||||
def processIncoming(self, data):
|
||||
if self._force_bitrate:
|
||||
time.sleep(len(data) / self.bitrate * 8)
|
||||
|
||||
self.rxb += len(data)
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||
self.parent_interface.rxb += len(data)
|
||||
@@ -165,8 +170,16 @@ class LocalClientInterface(Interface):
|
||||
if self.online:
|
||||
try:
|
||||
self.writing = True
|
||||
|
||||
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])
|
||||
self.socket.sendall(data)
|
||||
self.writing = False
|
||||
@@ -259,7 +272,8 @@ class LocalClientInterface(Interface):
|
||||
RNS.Transport.local_client_interfaces.remove(self)
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||
self.parent_interface.clients -= 1
|
||||
RNS.Transport.owner._should_persist_data()
|
||||
if hasattr(RNS.Transport, "owner") and RNS.Transport.owner != None:
|
||||
RNS.Transport.owner._should_persist_data()
|
||||
|
||||
if nowarning == False:
|
||||
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||
@@ -280,8 +294,7 @@ class LocalClientInterface(Interface):
|
||||
class LocalServerInterface(Interface):
|
||||
|
||||
def __init__(self, owner, bindport=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
self.online = False
|
||||
self.clients = 0
|
||||
|
||||
@@ -329,7 +342,9 @@ class LocalServerInterface(Interface):
|
||||
spawned_interface.target_port = str(handler.client_address[1])
|
||||
spawned_interface.parent_interface = self
|
||||
spawned_interface.bitrate = self.bitrate
|
||||
RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_EXTREME)
|
||||
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.Transport.interfaces.append(spawned_interface)
|
||||
RNS.Transport.local_client_interfaces.append(spawned_interface)
|
||||
self.clients += 1
|
||||
@@ -338,6 +353,12 @@ class LocalServerInterface(Interface):
|
||||
def processOutgoing(self, data):
|
||||
pass
|
||||
|
||||
def received_announce(self, from_spawned=False):
|
||||
if from_spawned: self.ia_freq_deque.append(time.time())
|
||||
|
||||
def sent_announce(self, from_spawned=False):
|
||||
if from_spawned: self.oa_freq_deque.append(time.time())
|
||||
|
||||
def __str__(self):
|
||||
return "Shared Instance["+str(self.bind_port)+"]"
|
||||
|
||||
|
||||
@@ -54,8 +54,7 @@ class PipeInterface(Interface):
|
||||
if respawn_delay == None:
|
||||
respawn_delay = 5
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -59,6 +59,7 @@ class KISS():
|
||||
CMD_FB_EXT = 0x41
|
||||
CMD_FB_READ = 0x42
|
||||
CMD_FB_WRITE = 0x43
|
||||
CMD_BT_CTRL = 0x46
|
||||
CMD_PLATFORM = 0x48
|
||||
CMD_MCU = 0x49
|
||||
CMD_FW_VERSION = 0x50
|
||||
@@ -91,7 +92,7 @@ class RNodeInterface(Interface):
|
||||
MAX_CHUNK = 32768
|
||||
|
||||
FREQ_MIN = 137000000
|
||||
FREQ_MAX = 1020000000
|
||||
FREQ_MAX = 3000000000
|
||||
|
||||
RSSI_OFFSET = 157
|
||||
|
||||
@@ -102,9 +103,13 @@ class RNodeInterface(Interface):
|
||||
|
||||
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):
|
||||
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
|
||||
if importlib.util.find_spec('serial') != None:
|
||||
@@ -114,8 +119,7 @@ class RNodeInterface(Interface):
|
||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||
RNS.panic()
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 508
|
||||
|
||||
@@ -186,15 +190,15 @@ class RNodeInterface(Interface):
|
||||
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
self.validcfg = False
|
||||
|
||||
@@ -672,6 +676,18 @@ class RNodeInterface(Interface):
|
||||
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
|
||||
elif (command == KISS.CMD_STAT_SNR):
|
||||
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):
|
||||
if (byte == KISS.FESC):
|
||||
escape = True
|
||||
@@ -783,7 +799,7 @@ class RNodeInterface(Interface):
|
||||
else:
|
||||
time_since_last = int(time.time()*1000) - last_read_ms
|
||||
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""
|
||||
in_frame = False
|
||||
command = KISS.CMD_UNKNOWN
|
||||
@@ -838,5 +854,8 @@ class RNodeInterface(Interface):
|
||||
self.setRadioState(KISS.RADIO_STATE_OFF)
|
||||
self.leave()
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "RNodeInterface["+str(self.name)+"]"
|
||||
|
||||
@@ -60,8 +60,7 @@ class SerialInterface(Interface):
|
||||
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||
RNS.panic()
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
@@ -201,5 +200,8 @@ class SerialInterface(Interface):
|
||||
|
||||
RNS.log("Reconnected serial port for "+str(self))
|
||||
|
||||
def should_ingress_limit(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "SerialInterface["+self.name+"]"
|
||||
|
||||
@@ -79,8 +79,7 @@ class TCPClientInterface(Interface):
|
||||
I2P_PROBES = 5
|
||||
|
||||
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
@@ -117,6 +116,8 @@ class TCPClientInterface(Interface):
|
||||
elif platform.system() == "Darwin":
|
||||
self.set_timeouts_osx()
|
||||
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
|
||||
elif target_ip != None and target_port != None:
|
||||
self.receives = True
|
||||
self.target_ip = target_ip
|
||||
@@ -201,6 +202,7 @@ class TCPClientInterface(Interface):
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
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.settimeout(None)
|
||||
self.online = True
|
||||
@@ -419,8 +421,7 @@ class TCPServerInterface(Interface):
|
||||
return ifaddr[netinfo.AF_INET][0]["broadcast"]
|
||||
|
||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
@@ -505,6 +506,12 @@ class TCPServerInterface(Interface):
|
||||
self.clients += 1
|
||||
spawned_interface.read_loop()
|
||||
|
||||
def received_announce(self, from_spawned=False):
|
||||
if from_spawned: self.ia_freq_deque.append(time.time())
|
||||
|
||||
def sent_announce(self, from_spawned=False):
|
||||
if from_spawned: self.oa_freq_deque.append(time.time())
|
||||
|
||||
def processOutgoing(self, data):
|
||||
pass
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@ class UDPInterface(Interface):
|
||||
return ifaddr[netinfo.AF_INET][0]["broadcast"]
|
||||
|
||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
super().__init__()
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
|
||||
+226
-132
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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.set_link_id(packet)
|
||||
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)
|
||||
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.attached_interface = packet.receiving_interface
|
||||
link.prove()
|
||||
@@ -123,7 +124,7 @@ class Link:
|
||||
link.last_inbound = time.time()
|
||||
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
|
||||
|
||||
except Exception as e:
|
||||
@@ -132,7 +133,7 @@ class Link:
|
||||
return None
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -149,10 +150,14 @@ class Link:
|
||||
self.last_inbound = 0
|
||||
self.last_outbound = 0
|
||||
self.last_proof = 0
|
||||
self.last_data = 0
|
||||
self.tx = 0
|
||||
self.rx = 0
|
||||
self.txbytes = 0
|
||||
self.rxbytes = 0
|
||||
self.rssi = None
|
||||
self.snr = None
|
||||
self.q = None
|
||||
self.traffic_timeout_factor = Link.TRAFFIC_TIMEOUT_FACTOR
|
||||
self.keepalive_timeout_factor = Link.KEEPALIVE_TIMEOUT_FACTOR
|
||||
self.keepalive = Link.KEEPALIVE
|
||||
@@ -165,6 +170,7 @@ class Link:
|
||||
self.destination = destination
|
||||
self.attached_interface = None
|
||||
self.__remote_identity = None
|
||||
self.__track_phy_stats = False
|
||||
self._channel = None
|
||||
if self.destination == None:
|
||||
self.initiator = False
|
||||
@@ -172,7 +178,8 @@ class Link:
|
||||
self.sig_prv = self.owner.identity.sig_prv
|
||||
else:
|
||||
self.initiator = True
|
||||
self.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, RNS.Transport.hops_to(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.sig_prv = Ed25519PrivateKey.generate()
|
||||
|
||||
@@ -208,6 +215,7 @@ class Link:
|
||||
self.packet.send()
|
||||
self.had_outbound()
|
||||
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):
|
||||
@@ -345,7 +353,7 @@ class Link:
|
||||
packed_request = umsgpack.packb(unpacked_request)
|
||||
|
||||
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:
|
||||
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
|
||||
@@ -385,24 +393,57 @@ class Link:
|
||||
try:
|
||||
measured_rtt = time.time() - self.request_time
|
||||
plaintext = self.decrypt(packet.data)
|
||||
rtt = umsgpack.unpackb(plaintext)
|
||||
self.rtt = max(measured_rtt, rtt)
|
||||
self.status = Link.ACTIVE
|
||||
self.activated_at = time.time()
|
||||
if plaintext != None:
|
||||
rtt = umsgpack.unpackb(plaintext)
|
||||
self.rtt = max(measured_rtt, rtt)
|
||||
self.status = Link.ACTIVE
|
||||
self.activated_at = time.time()
|
||||
|
||||
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
|
||||
self.establishment_rate = self.establishment_cost/self.rtt
|
||||
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
|
||||
self.establishment_rate = self.establishment_cost/self.rtt
|
||||
|
||||
try:
|
||||
if self.owner.callbacks.link_established != None:
|
||||
self.owner.callbacks.link_established(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
try:
|
||||
if self.owner.callbacks.link_established != None:
|
||||
self.owner.callbacks.link_established(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
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):
|
||||
"""
|
||||
:returns: The data transfer rate at which the link establishment procedure ocurred, in bits per second.
|
||||
@@ -420,7 +461,7 @@ class Link:
|
||||
|
||||
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
|
||||
last_inbound = max(self.last_inbound, activated_at)
|
||||
@@ -428,13 +469,19 @@ class Link:
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
"""
|
||||
: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())
|
||||
|
||||
@@ -444,8 +491,10 @@ class Link:
|
||||
"""
|
||||
return self.__remote_identity
|
||||
|
||||
def had_outbound(self):
|
||||
def had_outbound(self, is_keepalive=False):
|
||||
self.last_outbound = time.time()
|
||||
if not is_keepalive:
|
||||
self.last_data = self.last_outbound
|
||||
|
||||
def teardown(self):
|
||||
"""
|
||||
@@ -472,6 +521,7 @@ class Link:
|
||||
self.teardown_reason = Link.DESTINATION_CLOSED
|
||||
else:
|
||||
self.teardown_reason = Link.INITIATOR_CLOSED
|
||||
self.__update_phy_stats(packet)
|
||||
self.link_closed()
|
||||
except Exception as e:
|
||||
pass
|
||||
@@ -577,10 +627,25 @@ class Link:
|
||||
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):
|
||||
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
|
||||
keepalive_packet.send()
|
||||
self.had_outbound()
|
||||
self.had_outbound(is_keepalive = True)
|
||||
|
||||
def handle_request(self, request_id, unpacked_request):
|
||||
if self.status == Link.ACTIVE:
|
||||
@@ -631,7 +696,9 @@ class Link:
|
||||
remove = pending_request
|
||||
try:
|
||||
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)
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
@@ -684,135 +751,167 @@ class Link:
|
||||
RNS.log("Link-associated packet received on unexpected interface! Someone might be trying to manipulate your communication!", RNS.LOG_ERROR)
|
||||
else:
|
||||
self.last_inbound = time.time()
|
||||
if packet.context != RNS.Packet.KEEPALIVE:
|
||||
self.last_data = self.last_inbound
|
||||
self.rx += 1
|
||||
self.rxbytes += len(packet.data)
|
||||
if self.status == Link.STALE:
|
||||
self.status = Link.ACTIVE
|
||||
|
||||
if packet.packet_type == RNS.Packet.DATA:
|
||||
should_query = False
|
||||
if packet.context == RNS.Packet.NONE:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
if self.callbacks.packet != None:
|
||||
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
|
||||
packet.prove()
|
||||
if plaintext != None:
|
||||
if self.callbacks.packet != None:
|
||||
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
|
||||
packet.prove()
|
||||
should_query = True
|
||||
|
||||
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
|
||||
if self.destination.callbacks.proof_requested:
|
||||
try:
|
||||
self.destination.callbacks.proof_requested(packet)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
|
||||
if self.destination.callbacks.proof_requested:
|
||||
try:
|
||||
if self.destination.callbacks.proof_requested(packet):
|
||||
packet.prove()
|
||||
should_query = True
|
||||
except Exception as e:
|
||||
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:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
if plaintext != None:
|
||||
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
|
||||
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
|
||||
signed_data = self.link_id+public_key
|
||||
signature = plaintext[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Identity.SIGLENGTH//8]
|
||||
identity = RNS.Identity(create_keys=False)
|
||||
identity.load_public_key(public_key)
|
||||
|
||||
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
|
||||
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
|
||||
signed_data = self.link_id+public_key
|
||||
signature = plaintext[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Identity.SIGLENGTH//8]
|
||||
identity = RNS.Identity(create_keys=False)
|
||||
identity.load_public_key(public_key)
|
||||
|
||||
if identity.validate(signature, signed_data):
|
||||
self.__remote_identity = identity
|
||||
if self.callbacks.remote_identified != None:
|
||||
try:
|
||||
self.callbacks.remote_identified(self, self.__remote_identity)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
if identity.validate(signature, signed_data):
|
||||
self.__remote_identity = identity
|
||||
if self.callbacks.remote_identified != None:
|
||||
try:
|
||||
self.callbacks.remote_identified(self, self.__remote_identity)
|
||||
except Exception as e:
|
||||
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:
|
||||
try:
|
||||
request_id = packet.getTruncatedHash()
|
||||
packed_request = self.decrypt(packet.data)
|
||||
unpacked_request = umsgpack.unpackb(packed_request)
|
||||
self.handle_request(request_id, unpacked_request)
|
||||
if packed_request != None:
|
||||
unpacked_request = umsgpack.unpackb(packed_request)
|
||||
self.handle_request(request_id, unpacked_request)
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
elif packet.context == RNS.Packet.RESPONSE:
|
||||
try:
|
||||
packed_response = self.decrypt(packet.data)
|
||||
unpacked_response = umsgpack.unpackb(packed_response)
|
||||
request_id = unpacked_response[0]
|
||||
response_data = unpacked_response[1]
|
||||
transfer_size = len(umsgpack.packb(response_data))-2
|
||||
self.handle_response(request_id, response_data, transfer_size, transfer_size)
|
||||
if packed_response != None:
|
||||
unpacked_response = umsgpack.unpackb(packed_response)
|
||||
request_id = unpacked_response[0]
|
||||
response_data = unpacked_response[1]
|
||||
transfer_size = len(umsgpack.packb(response_data))-2
|
||||
self.handle_response(request_id, response_data, transfer_size, transfer_size)
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
except Exception as e:
|
||||
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
elif packet.context == RNS.Packet.LRRTT:
|
||||
if not self.initiator:
|
||||
self.rtt_packet(packet)
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
|
||||
elif packet.context == RNS.Packet.LINKCLOSE:
|
||||
self.teardown_packet(packet)
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
|
||||
elif packet.context == RNS.Packet.RESOURCE_ADV:
|
||||
packet.plaintext = self.decrypt(packet.data)
|
||||
if packet.plaintext != None:
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
|
||||
if RNS.ResourceAdvertisement.is_request(packet):
|
||||
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
|
||||
elif RNS.ResourceAdvertisement.is_response(packet):
|
||||
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
|
||||
for pending_request in self.pending_requests:
|
||||
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)
|
||||
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
|
||||
pending_request.response_transfer_size = RNS.ResourceAdvertisement.read_transfer_size(packet)
|
||||
pending_request.started_at = time.time()
|
||||
elif self.resource_strategy == Link.ACCEPT_NONE:
|
||||
pass
|
||||
elif self.resource_strategy == Link.ACCEPT_APP:
|
||||
if self.callbacks.resource != None:
|
||||
try:
|
||||
resource_advertisement = RNS.ResourceAdvertisement.unpack(packet.plaintext)
|
||||
resource_advertisement.link = self
|
||||
if self.callbacks.resource(resource_advertisement):
|
||||
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
elif self.resource_strategy == Link.ACCEPT_ALL:
|
||||
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
|
||||
if RNS.ResourceAdvertisement.is_request(packet):
|
||||
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
|
||||
elif RNS.ResourceAdvertisement.is_response(packet):
|
||||
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
|
||||
for pending_request in self.pending_requests:
|
||||
if pending_request.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)
|
||||
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.response_resource_progress(response_resource)
|
||||
|
||||
elif self.resource_strategy == Link.ACCEPT_NONE:
|
||||
pass
|
||||
elif self.resource_strategy == Link.ACCEPT_APP:
|
||||
if self.callbacks.resource != None:
|
||||
try:
|
||||
resource_advertisement = RNS.ResourceAdvertisement.unpack(packet.plaintext)
|
||||
resource_advertisement.link = self
|
||||
if self.callbacks.resource(resource_advertisement):
|
||||
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
elif self.resource_strategy == Link.ACCEPT_ALL:
|
||||
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
|
||||
|
||||
elif packet.context == RNS.Packet.RESOURCE_REQ:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
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]
|
||||
else:
|
||||
resource_hash = plaintext[1:RNS.Identity.HASHLENGTH//8+1]
|
||||
if plaintext != None:
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
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]
|
||||
else:
|
||||
resource_hash = plaintext[1:RNS.Identity.HASHLENGTH//8+1]
|
||||
|
||||
for resource in self.outgoing_resources:
|
||||
if resource.hash == resource_hash:
|
||||
# We need to check that this request has not been
|
||||
# received before in order to avoid sequencing errors.
|
||||
if not packet.packet_hash in resource.req_hashlist:
|
||||
resource.req_hashlist.append(packet.packet_hash)
|
||||
resource.request(plaintext)
|
||||
for resource in self.outgoing_resources:
|
||||
if resource.hash == resource_hash:
|
||||
# We need to check that this request has not been
|
||||
# received before in order to avoid sequencing errors.
|
||||
if not packet.packet_hash in resource.req_hashlist:
|
||||
resource.req_hashlist.append(packet.packet_hash)
|
||||
resource.request(plaintext)
|
||||
|
||||
elif packet.context == RNS.Packet.RESOURCE_HMU:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
||||
for resource in self.incoming_resources:
|
||||
if resource_hash == resource.hash:
|
||||
resource.hashmap_update_packet(plaintext)
|
||||
if plaintext != None:
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
||||
for resource in self.incoming_resources:
|
||||
if resource_hash == resource.hash:
|
||||
resource.hashmap_update_packet(plaintext)
|
||||
|
||||
elif packet.context == RNS.Packet.RESOURCE_ICL:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
||||
for resource in self.incoming_resources:
|
||||
if resource_hash == resource.hash:
|
||||
resource.cancel()
|
||||
if plaintext != None:
|
||||
self.__update_phy_stats(packet)
|
||||
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
|
||||
for resource in self.incoming_resources:
|
||||
if resource_hash == resource.hash:
|
||||
resource.cancel()
|
||||
|
||||
elif packet.context == RNS.Packet.KEEPALIVE:
|
||||
if not self.initiator and packet.data == bytes([0xFF]):
|
||||
keepalive_packet = RNS.Packet(self, bytes([0xFE]), context=RNS.Packet.KEEPALIVE)
|
||||
keepalive_packet.send()
|
||||
self.had_outbound()
|
||||
self.had_outbound(is_keepalive = True)
|
||||
|
||||
|
||||
# TODO: find the most efficient way to allow multiple
|
||||
@@ -822,27 +921,17 @@ class Link:
|
||||
elif packet.context == RNS.Packet.RESOURCE:
|
||||
for resource in self.incoming_resources:
|
||||
resource.receive_part(packet)
|
||||
self.__update_phy_stats(packet)
|
||||
|
||||
elif packet.context == RNS.Packet.CHANNEL:
|
||||
if not self._channel:
|
||||
RNS.log(f"Channel data received without open channel", RNS.LOG_DEBUG)
|
||||
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()
|
||||
plaintext = self.decrypt(packet.data)
|
||||
self._channel._receive(plaintext)
|
||||
if plaintext != None:
|
||||
self.__update_phy_stats(packet)
|
||||
self._channel._receive(plaintext)
|
||||
|
||||
elif packet.packet_type == RNS.Packet.PROOF:
|
||||
if packet.context == RNS.Packet.RESOURCE_PRF:
|
||||
@@ -850,6 +939,7 @@ class Link:
|
||||
for resource in self.outgoing_resources:
|
||||
if resource_hash == resource.hash:
|
||||
resource.validate_proof(packet.data)
|
||||
self.__update_phy_stats(packet, query_shared=True)
|
||||
|
||||
self.watchdog_lock = False
|
||||
|
||||
@@ -879,6 +969,7 @@ class Link:
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
|
||||
def sign(self, message):
|
||||
@@ -1059,7 +1150,8 @@ class RequestReceipt():
|
||||
def request_resource_concluded(self, resource):
|
||||
if resource.status == RNS.Resource.COMPLETE:
|
||||
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
|
||||
self.started_at = time.time()
|
||||
if self.started_at == None:
|
||||
self.started_at = time.time()
|
||||
self.status = RequestReceipt.DELIVERED
|
||||
self.__resource_response_timeout = time.time()+self.timeout
|
||||
response_timeout_thread = threading.Thread(target=self.__response_timeout_job)
|
||||
@@ -1100,24 +1192,26 @@ class RequestReceipt():
|
||||
|
||||
|
||||
def response_resource_progress(self, resource):
|
||||
if not self.status == RequestReceipt.FAILED:
|
||||
self.status = RequestReceipt.RECEIVING
|
||||
if self.packet_receipt != None:
|
||||
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
|
||||
self.packet_receipt.proved = True
|
||||
self.packet_receipt.concluded_at = time.time()
|
||||
if self.packet_receipt.callbacks.delivery != None:
|
||||
self.packet_receipt.callbacks.delivery(self.packet_receipt)
|
||||
if resource != None:
|
||||
if not self.status == RequestReceipt.FAILED:
|
||||
self.status = RequestReceipt.RECEIVING
|
||||
if self.packet_receipt != None:
|
||||
if self.packet_receipt.status != RNS.PacketReceipt.DELIVERED:
|
||||
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
|
||||
self.packet_receipt.proved = True
|
||||
self.packet_receipt.concluded_at = time.time()
|
||||
if self.packet_receipt.callbacks.delivery != None:
|
||||
self.packet_receipt.callbacks.delivery(self.packet_receipt)
|
||||
|
||||
self.progress = resource.get_progress()
|
||||
|
||||
if self.callbacks.progress != None:
|
||||
try:
|
||||
self.callbacks.progress(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
else:
|
||||
resource.cancel()
|
||||
self.progress = resource.get_progress()
|
||||
|
||||
if self.callbacks.progress != None:
|
||||
try:
|
||||
self.callbacks.progress(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
else:
|
||||
resource.cancel()
|
||||
|
||||
|
||||
def response_received(self, response):
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,7 +30,7 @@ class Packet:
|
||||
"""
|
||||
The Packet class is used to create packet instances that can be sent
|
||||
over a Reticulum network. Packets will automatically be encrypted if
|
||||
they are adressed to a ``RNS.Destination.SINGLE`` destination,
|
||||
they are addressed to a ``RNS.Destination.SINGLE`` destination,
|
||||
``RNS.Destination.GROUP`` destination or a :ref:`RNS.Link<api-link>`.
|
||||
|
||||
For ``RNS.Destination.GROUP`` destinations, Reticulum will use the
|
||||
@@ -138,6 +138,7 @@ class Packet:
|
||||
self.receiving_interface = None
|
||||
self.rssi = None
|
||||
self.snr = None
|
||||
self.q = None
|
||||
|
||||
def get_packed_flags(self):
|
||||
if self.context == Packet.LRPROOF:
|
||||
@@ -370,8 +371,8 @@ class PacketReceipt:
|
||||
if packet.destination.type == RNS.Destination.LINK:
|
||||
self.timeout = packet.destination.rtt * packet.destination.traffic_timeout_factor
|
||||
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):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
class Resolver:
|
||||
|
||||
@staticmethod
|
||||
def resolve_identity(full_name):
|
||||
pass
|
||||
+123
-97
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,7 +25,9 @@ import os
|
||||
import bz2
|
||||
import math
|
||||
import time
|
||||
import tempfile
|
||||
import threading
|
||||
from threading import Lock
|
||||
from .vendor import umsgpack as umsgpack
|
||||
from time import sleep
|
||||
|
||||
@@ -47,7 +49,7 @@ class Resource:
|
||||
WINDOW = 4
|
||||
|
||||
# Absolute minimum window size during transfer
|
||||
WINDOW_MIN = 1
|
||||
WINDOW_MIN = 2
|
||||
|
||||
# The maximum window size for transfers on slow links
|
||||
WINDOW_MAX_SLOW = 10
|
||||
@@ -103,9 +105,10 @@ class Resource:
|
||||
|
||||
PART_TIMEOUT_FACTOR = 4
|
||||
PART_TIMEOUT_FACTOR_AFTER_RTT = 2
|
||||
MAX_RETRIES = 8
|
||||
MAX_RETRIES = 16
|
||||
MAX_ADV_RETRIES = 4
|
||||
SENDER_GRACE_TIME = 10
|
||||
SENDER_GRACE_TIME = 10.0
|
||||
PROCESSING_GRACE = 1.0
|
||||
RETRY_GRACE_TIME = 0.25
|
||||
PER_RETRY_DELAY = 0.5
|
||||
|
||||
@@ -167,15 +170,13 @@ class Resource:
|
||||
resource.hashmap = [None] * resource.total_parts
|
||||
resource.hashmap_height = 0
|
||||
resource.waiting_for_hmu = False
|
||||
|
||||
resource.receiving_part = False
|
||||
|
||||
resource.consecutive_completed_height = 0
|
||||
resource.consecutive_completed_height = -1
|
||||
|
||||
if not resource.link.has_incoming_resource(resource):
|
||||
resource.link.register_incoming_resource(resource)
|
||||
|
||||
RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
|
||||
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:
|
||||
try:
|
||||
resource.link.callbacks.resource_started(resource)
|
||||
@@ -204,9 +205,19 @@ class Resource:
|
||||
resource_data = None
|
||||
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"):
|
||||
data_size = os.stat(data.name).st_size
|
||||
self.total_size = data_size
|
||||
if data_size == None:
|
||||
data_size = os.stat(data.name).st_size
|
||||
|
||||
self.total_size = data_size
|
||||
self.grand_total_parts = math.ceil(data_size/Resource.SDU)
|
||||
|
||||
if data_size <= Resource.MAX_EFFICIENT_SIZE:
|
||||
@@ -279,7 +290,7 @@ class Resource:
|
||||
self.uncompressed_data = data
|
||||
|
||||
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)
|
||||
self.compressed_data = bz2.compress(self.uncompressed_data)
|
||||
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
|
||||
@@ -366,7 +377,8 @@ class Resource:
|
||||
if advertise:
|
||||
self.advertise()
|
||||
else:
|
||||
pass
|
||||
self.receive_lock = Lock()
|
||||
|
||||
|
||||
def hashmap_update_packet(self, plaintext):
|
||||
if not self.status == Resource.FAILED:
|
||||
@@ -440,7 +452,7 @@ class Resource:
|
||||
sleep_time = None
|
||||
|
||||
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 self.retries_left <= 0:
|
||||
RNS.log("Resource transfer timeout after sending advertisement", RNS.LOG_DEBUG)
|
||||
@@ -456,7 +468,7 @@ class Resource:
|
||||
self.adv_sent = self.last_activity
|
||||
sleep_time = 0.001
|
||||
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()
|
||||
|
||||
|
||||
@@ -612,10 +624,26 @@ class Resource:
|
||||
self.callback(self)
|
||||
except Exception as e:
|
||||
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:
|
||||
# Otherwise we'll recursively create the
|
||||
# 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:
|
||||
pass
|
||||
else:
|
||||
@@ -623,99 +651,99 @@ class Resource:
|
||||
|
||||
|
||||
def receive_part(self, packet):
|
||||
while self.receiving_part:
|
||||
sleep(0.001)
|
||||
with self.receive_lock:
|
||||
|
||||
self.receiving_part = True
|
||||
self.last_activity = time.time()
|
||||
self.retries_left = self.max_retries
|
||||
self.receiving_part = True
|
||||
self.last_activity = time.time()
|
||||
self.retries_left = self.max_retries
|
||||
|
||||
if self.req_resp == None:
|
||||
self.req_resp = self.last_activity
|
||||
rtt = self.req_resp-self.req_sent
|
||||
|
||||
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
|
||||
if self.rtt == None:
|
||||
self.rtt = self.link.rtt
|
||||
self.watchdog_job()
|
||||
elif rtt < self.rtt:
|
||||
self.rtt = max(self.rtt - self.rtt*0.05, rtt)
|
||||
elif rtt > self.rtt:
|
||||
self.rtt = min(self.rtt + self.rtt*0.05, rtt)
|
||||
if self.req_resp == None:
|
||||
self.req_resp = self.last_activity
|
||||
rtt = self.req_resp-self.req_sent
|
||||
|
||||
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
|
||||
if self.rtt == None:
|
||||
self.rtt = self.link.rtt
|
||||
self.watchdog_job()
|
||||
elif rtt < self.rtt:
|
||||
self.rtt = max(self.rtt - self.rtt*0.05, rtt)
|
||||
elif rtt > self.rtt:
|
||||
self.rtt = min(self.rtt + self.rtt*0.05, rtt)
|
||||
|
||||
if rtt > 0:
|
||||
req_resp_cost = len(packet.raw)+self.req_sent_bytes
|
||||
self.req_resp_rtt_rate = req_resp_cost / rtt
|
||||
if rtt > 0:
|
||||
req_resp_cost = len(packet.raw)+self.req_sent_bytes
|
||||
self.req_resp_rtt_rate = req_resp_cost / rtt
|
||||
|
||||
if self.req_resp_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
|
||||
self.fast_rate_rounds += 1
|
||||
if self.req_resp_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
|
||||
self.fast_rate_rounds += 1
|
||||
|
||||
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
|
||||
self.window_max = Resource.WINDOW_MAX_FAST
|
||||
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
|
||||
self.window_max = Resource.WINDOW_MAX_FAST
|
||||
|
||||
if not self.status == Resource.FAILED:
|
||||
self.status = Resource.TRANSFERRING
|
||||
part_data = packet.data
|
||||
part_hash = self.get_map_hash(part_data)
|
||||
if not self.status == Resource.FAILED:
|
||||
self.status = Resource.TRANSFERRING
|
||||
part_data = packet.data
|
||||
part_hash = self.get_map_hash(part_data)
|
||||
|
||||
i = self.consecutive_completed_height
|
||||
for map_hash in self.hashmap[self.consecutive_completed_height:self.consecutive_completed_height+self.window]:
|
||||
if map_hash == part_hash:
|
||||
if self.parts[i] == None:
|
||||
consecutive_index = self.consecutive_completed_height if self.consecutive_completed_height >= 0 else 0
|
||||
i = consecutive_index
|
||||
for map_hash in self.hashmap[consecutive_index:consecutive_index+self.window]:
|
||||
if map_hash == part_hash:
|
||||
if self.parts[i] == None:
|
||||
|
||||
# Insert data into parts list
|
||||
self.parts[i] = part_data
|
||||
self.rtt_rxd_bytes += len(part_data)
|
||||
self.received_count += 1
|
||||
self.outstanding_parts -= 1
|
||||
# Insert data into parts list
|
||||
self.parts[i] = part_data
|
||||
self.rtt_rxd_bytes += len(part_data)
|
||||
self.received_count += 1
|
||||
self.outstanding_parts -= 1
|
||||
|
||||
# Update consecutive completed pointer
|
||||
if i == self.consecutive_completed_height + 1:
|
||||
self.consecutive_completed_height = i
|
||||
|
||||
cp = self.consecutive_completed_height + 1
|
||||
while cp < len(self.parts) and self.parts[cp] != None:
|
||||
self.consecutive_completed_height = cp
|
||||
cp += 1
|
||||
# Update consecutive completed pointer
|
||||
if i == self.consecutive_completed_height + 1:
|
||||
self.consecutive_completed_height = i
|
||||
|
||||
cp = self.consecutive_completed_height + 1
|
||||
while cp < len(self.parts) and self.parts[cp] != None:
|
||||
self.consecutive_completed_height = cp
|
||||
cp += 1
|
||||
|
||||
if self.__progress_callback != None:
|
||||
try:
|
||||
self.__progress_callback(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
if self.__progress_callback != None:
|
||||
try:
|
||||
self.__progress_callback(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
i += 1
|
||||
i += 1
|
||||
|
||||
self.receiving_part = False
|
||||
self.receiving_part = False
|
||||
|
||||
if self.received_count == self.total_parts and not self.assembly_lock:
|
||||
self.assembly_lock = True
|
||||
self.assemble()
|
||||
elif self.outstanding_parts == 0:
|
||||
# TODO: Figure out if there is a mathematically
|
||||
# optimal way to adjust windows
|
||||
if self.window < self.window_max:
|
||||
self.window += 1
|
||||
if (self.window - self.window_min) > (self.window_flexibility-1):
|
||||
self.window_min += 1
|
||||
if self.received_count == self.total_parts and not self.assembly_lock:
|
||||
self.assembly_lock = True
|
||||
self.assemble()
|
||||
elif self.outstanding_parts == 0:
|
||||
# TODO: Figure out if there is a mathematically
|
||||
# optimal way to adjust windows
|
||||
if self.window < self.window_max:
|
||||
self.window += 1
|
||||
if (self.window - self.window_min) > (self.window_flexibility-1):
|
||||
self.window_min += 1
|
||||
|
||||
if self.req_sent != 0:
|
||||
rtt = time.time()-self.req_sent
|
||||
req_transferred = self.rtt_rxd_bytes - self.rtt_rxd_bytes_at_part_req
|
||||
if self.req_sent != 0:
|
||||
rtt = time.time()-self.req_sent
|
||||
req_transferred = self.rtt_rxd_bytes - self.rtt_rxd_bytes_at_part_req
|
||||
|
||||
if rtt != 0:
|
||||
self.req_data_rtt_rate = req_transferred/rtt
|
||||
self.rtt_rxd_bytes_at_part_req = self.rtt_rxd_bytes
|
||||
if rtt != 0:
|
||||
self.req_data_rtt_rate = req_transferred/rtt
|
||||
self.rtt_rxd_bytes_at_part_req = self.rtt_rxd_bytes
|
||||
|
||||
if self.req_data_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
|
||||
self.fast_rate_rounds += 1
|
||||
if self.req_data_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
|
||||
self.fast_rate_rounds += 1
|
||||
|
||||
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
|
||||
self.window_max = Resource.WINDOW_MAX_FAST
|
||||
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
|
||||
self.window_max = Resource.WINDOW_MAX_FAST
|
||||
|
||||
self.request_next()
|
||||
else:
|
||||
self.receiving_part = False
|
||||
self.request_next()
|
||||
else:
|
||||
self.receiving_part = False
|
||||
|
||||
# Called on incoming resource to send a request for more data
|
||||
def request_next(self):
|
||||
@@ -728,11 +756,11 @@ class Resource:
|
||||
hashmap_exhausted = Resource.HASHMAP_IS_NOT_EXHAUSTED
|
||||
requested_hashes = b""
|
||||
|
||||
offset = (1 if self.consecutive_completed_height > 0 else 0)
|
||||
i = 0; pn = self.consecutive_completed_height+offset
|
||||
i = 0; pn = self.consecutive_completed_height+1
|
||||
search_start = pn
|
||||
|
||||
for part in self.parts[search_start:search_start+self.window]:
|
||||
search_size = self.window
|
||||
|
||||
for part in self.parts[search_start:search_start+search_size]:
|
||||
if part == None:
|
||||
part_hash = self.hashmap[pn]
|
||||
if part_hash != None:
|
||||
@@ -752,7 +780,6 @@ class Resource:
|
||||
hmu_part += last_map_hash
|
||||
self.waiting_for_hmu = True
|
||||
|
||||
requested_data = b""
|
||||
request_data = hmu_part + self.hash + requested_hashes
|
||||
request_packet = RNS.Packet(self.link, request_data, context = RNS.Packet.RESOURCE_REQ)
|
||||
|
||||
@@ -908,8 +935,7 @@ class Resource:
|
||||
else:
|
||||
self.progress_total_parts = float(self.total_parts)
|
||||
|
||||
|
||||
progress = self.processed_parts / self.progress_total_parts
|
||||
progress = min(1.0, self.processed_parts / self.progress_total_parts)
|
||||
return progress
|
||||
|
||||
def get_transfer_size(self):
|
||||
|
||||
+135
-11
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -79,7 +79,7 @@ class Reticulum:
|
||||
MTU = 500
|
||||
"""
|
||||
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
|
||||
completely break compatibility with all other RNS networks. An identical
|
||||
MTU is a prerequisite for peers to communicate in the same network.
|
||||
@@ -106,17 +106,19 @@ class Reticulum:
|
||||
it will eventually be dropped.
|
||||
|
||||
This value will be applied by default to all created interfaces,
|
||||
but it can be configured individually on a per-interface basis.
|
||||
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
|
||||
# performance on faster links, we need a mechanism for setting
|
||||
# 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.
|
||||
# TODO: Let Reticulum somehow continously build a map of per-hop
|
||||
# latencies and use this map for global timeout calculation.
|
||||
DEFAULT_PER_HOP_TIMEOUT = 6
|
||||
|
||||
# Length of truncated hashes in bits.
|
||||
@@ -145,6 +147,8 @@ class Reticulum:
|
||||
configpath = ""
|
||||
storagepath = ""
|
||||
cachepath = ""
|
||||
|
||||
__instance = None
|
||||
|
||||
@staticmethod
|
||||
def exit_handler():
|
||||
@@ -168,6 +172,13 @@ class Reticulum:
|
||||
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):
|
||||
"""
|
||||
Initialises and starts a Reticulum instance. This must be
|
||||
@@ -177,6 +188,11 @@ class Reticulum:
|
||||
: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()
|
||||
|
||||
if configdir != None:
|
||||
@@ -209,6 +225,7 @@ class Reticulum:
|
||||
self.local_control_port = 37429
|
||||
self.share_instance = True
|
||||
self.rpc_listener = None
|
||||
self.rpc_key = None
|
||||
|
||||
self.ifac_salt = Reticulum.IFAC_SALT
|
||||
|
||||
@@ -262,7 +279,8 @@ class Reticulum:
|
||||
RNS.Transport.start(self)
|
||||
|
||||
self.rpc_addr = ("127.0.0.1", self.local_control_port)
|
||||
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
||||
if self.rpc_key == None:
|
||||
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
||||
|
||||
if self.is_shared_instance:
|
||||
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
|
||||
@@ -301,6 +319,10 @@ class Reticulum:
|
||||
self.local_interface_port
|
||||
)
|
||||
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)
|
||||
|
||||
self.is_shared_instance = True
|
||||
@@ -315,6 +337,10 @@ class Reticulum:
|
||||
self.local_interface_port)
|
||||
interface.target_port = self.local_interface_port
|
||||
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)
|
||||
self.is_shared_instance = False
|
||||
self.is_standalone_instance = False
|
||||
@@ -359,6 +385,13 @@ class Reticulum:
|
||||
if option == "instance_control_port":
|
||||
value = int(self.config["reticulum"][option])
|
||||
self.local_control_port = value
|
||||
if option == "rpc_key":
|
||||
try:
|
||||
value = bytes.fromhex(self.config["reticulum"][option])
|
||||
self.rpc_key = value
|
||||
except Exception as e:
|
||||
RNS.log("Invalid shared instance RPC key specified, falling back to default key", RNS.LOG_ERROR)
|
||||
self.rpc_key = None
|
||||
if option == "enable_transport":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True:
|
||||
@@ -367,6 +400,9 @@ class Reticulum:
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == 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":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True:
|
||||
@@ -447,6 +483,23 @@ class Reticulum:
|
||||
if c["pass_phrase"] != "":
|
||||
ifac_netkey = c["pass_phrase"]
|
||||
|
||||
ingress_control = True
|
||||
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
|
||||
ic_max_held_announces = None
|
||||
if "ic_max_held_announces" in c: ic_max_held_announces = c.as_int("ic_max_held_announces")
|
||||
ic_burst_hold = None
|
||||
if "ic_burst_hold" in c: ic_burst_hold = c.as_float("ic_burst_hold")
|
||||
ic_burst_freq_new = None
|
||||
if "ic_burst_freq_new" in c: ic_burst_freq_new = c.as_float("ic_burst_freq_new")
|
||||
ic_burst_freq = None
|
||||
if "ic_burst_freq" in c: ic_burst_freq = c.as_float("ic_burst_freq")
|
||||
ic_new_time = None
|
||||
if "ic_new_time" in c: ic_new_time = c.as_float("ic_new_time")
|
||||
ic_burst_penalty = None
|
||||
if "ic_burst_penalty" in c: ic_burst_penalty = c.as_float("ic_burst_penalty")
|
||||
ic_held_release_interval = None
|
||||
if "ic_held_release_interval" in c: ic_held_release_interval = c.as_float("ic_held_release_interval")
|
||||
|
||||
configured_bitrate = None
|
||||
if "bitrate" in c:
|
||||
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
|
||||
@@ -884,6 +937,14 @@ class Reticulum:
|
||||
interface.announce_rate_target = announce_rate_target
|
||||
interface.announce_rate_grace = announce_rate_grace
|
||||
interface.announce_rate_penalty = announce_rate_penalty
|
||||
interface.ingress_control = ingress_control
|
||||
if ic_max_held_announces != None: interface.ic_max_held_announces = ic_max_held_announces
|
||||
if ic_burst_hold != None: interface.ic_burst_hold = ic_burst_hold
|
||||
if ic_burst_freq_new != None: interface.ic_burst_freq_new = ic_burst_freq_new
|
||||
if ic_burst_freq != None: interface.ic_burst_freq = ic_burst_freq
|
||||
if ic_new_time != None: interface.ic_new_time = ic_new_time
|
||||
if ic_burst_penalty != None: interface.ic_burst_penalty = ic_burst_penalty
|
||||
if ic_held_release_interval != None: interface.ic_held_release_interval = ic_held_release_interval
|
||||
|
||||
interface.ifac_netname = ifac_netname
|
||||
interface.ifac_netkey = ifac_netkey
|
||||
@@ -1040,18 +1101,27 @@ class Reticulum:
|
||||
if path == "next_hop":
|
||||
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":
|
||||
rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
|
||||
|
||||
if path == "packet_snr":
|
||||
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:
|
||||
path = call["drop"]
|
||||
|
||||
if path == "path":
|
||||
rpc_connection.send(self.drop_path(call["destination_hash"]))
|
||||
|
||||
if path == "all_via":
|
||||
rpc_connection.send(self.drop_all_via(call["destination_hash"]))
|
||||
|
||||
if path == "announce_queues":
|
||||
rpc_connection.send(self.drop_announce_queues())
|
||||
|
||||
@@ -1143,6 +1213,9 @@ class Reticulum:
|
||||
ifstats["name"] = str(interface)
|
||||
ifstats["rxb"] = interface.rxb
|
||||
ifstats["txb"] = interface.txb
|
||||
ifstats["incoming_announce_frequency"] = interface.incoming_announce_frequency()
|
||||
ifstats["outgoing_announce_frequency"] = interface.outgoing_announce_frequency()
|
||||
ifstats["held_announces"] = len(interface.held_announces)
|
||||
ifstats["status"] = interface.online
|
||||
ifstats["mode"] = interface.mode
|
||||
|
||||
@@ -1213,6 +1286,22 @@ class Reticulum:
|
||||
else:
|
||||
return RNS.Transport.expire_path(destination)
|
||||
|
||||
def drop_all_via(self, transport_hash):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
rpc_connection.send({"drop": "all_via", "destination_hash": transport_hash})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else:
|
||||
dropped_count = 0
|
||||
for destination_hash in RNS.Transport.destination_table:
|
||||
if RNS.Transport.destination_table[destination_hash][1] == transport_hash:
|
||||
RNS.Transport.expire_path(destination_hash)
|
||||
dropped_count += 1
|
||||
|
||||
return dropped_count
|
||||
|
||||
def drop_announce_queues(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
@@ -1233,6 +1322,26 @@ class Reticulum:
|
||||
else:
|
||||
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):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
@@ -1272,6 +1381,21 @@ class Reticulum:
|
||||
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
|
||||
def should_use_implicit_proof():
|
||||
"""
|
||||
|
||||
+247
-69
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -66,13 +66,19 @@ class Transport:
|
||||
PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds
|
||||
PATH_REQUEST_GRACE = 0.35 # Grace time before a path announcement is made, allows directly reachable peers to respond first
|
||||
PATH_REQUEST_RW = 2 # Path request random window
|
||||
PATH_REQUEST_MI = 5 # Minimum interval in seconds for automated path requests
|
||||
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
|
||||
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
|
||||
MAX_RECEIPTS = 1024 # Maximum number of receipts to keep track of
|
||||
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
|
||||
destinations = [] # All active destinations
|
||||
@@ -94,6 +100,7 @@ class Transport:
|
||||
tunnels = {} # A table storing tunnels to other transport instances
|
||||
announce_rate_table = {} # A table for keeping track of announce rates
|
||||
path_requests = {} # A table for storing path request timestamps
|
||||
path_states = {} # A table for keeping track of path states
|
||||
|
||||
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
||||
discovery_pr_tags = [] # A table for keeping track of tagged path requests
|
||||
@@ -111,6 +118,7 @@ class Transport:
|
||||
|
||||
local_client_rssi_cache = []
|
||||
local_client_snr_cache = []
|
||||
local_client_q_cache = []
|
||||
LOCAL_CLIENT_CACHE_MAXSIZE = 512
|
||||
|
||||
pending_local_path_requests = {}
|
||||
@@ -128,6 +136,8 @@ class Transport:
|
||||
hashlist_maxsize = 1000000
|
||||
tables_last_culled = 0.0
|
||||
tables_cull_interval = 5.0
|
||||
interface_last_jobs = 0.0
|
||||
interface_jobs_interval = 5.0
|
||||
|
||||
identity = None
|
||||
|
||||
@@ -297,7 +307,8 @@ class Transport:
|
||||
@staticmethod
|
||||
def jobs():
|
||||
outgoing = []
|
||||
path_requests = []
|
||||
path_requests = {}
|
||||
blocked_if = None
|
||||
Transport.jobs_running = True
|
||||
|
||||
try:
|
||||
@@ -325,7 +336,8 @@ class Transport:
|
||||
if time.time() - last_path_request > Transport.PATH_REQUEST_MI:
|
||||
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link.destination.hash)+" since an attempted link was never established", RNS.LOG_DEBUG)
|
||||
if not link.destination.hash in path_requests:
|
||||
path_requests.append(link.destination.hash)
|
||||
blocked_if = None
|
||||
path_requests[link.destination.hash] = blocked_if
|
||||
|
||||
Transport.pending_links.remove(link)
|
||||
|
||||
@@ -352,12 +364,12 @@ class Transport:
|
||||
|
||||
# Process announces needing retransmission
|
||||
if time.time() > Transport.announces_last_checked+Transport.announces_check_interval:
|
||||
completed_announces = []
|
||||
for destination_hash in Transport.announce_table:
|
||||
announce_entry = Transport.announce_table[destination_hash]
|
||||
if announce_entry[2] > Transport.PATHFINDER_R:
|
||||
RNS.log("Completed announce processing for "+RNS.prettyhexrep(destination_hash)+", retry limit reached", RNS.LOG_EXTREME)
|
||||
Transport.announce_table.pop(destination_hash)
|
||||
break
|
||||
completed_announces.append(destination_hash)
|
||||
else:
|
||||
if time.time() > announce_entry[1]:
|
||||
announce_entry[1] = time.time() + Transport.PATHFINDER_G + Transport.PATHFINDER_RW
|
||||
@@ -404,6 +416,10 @@ class Transport:
|
||||
Transport.announce_table[destination_hash] = held_entry
|
||||
RNS.log("Reinserting held announce into table", RNS.LOG_DEBUG)
|
||||
|
||||
for destination_hash in completed_announces:
|
||||
if destination_hash in Transport.announce_table:
|
||||
Transport.announce_table.pop(destination_hash)
|
||||
|
||||
Transport.announces_last_checked = time.time()
|
||||
|
||||
|
||||
@@ -416,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]
|
||||
|
||||
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
|
||||
stale_reverse_entries = []
|
||||
for truncated_packet_hash in Transport.reverse_table:
|
||||
@@ -464,18 +486,24 @@ class Transport:
|
||||
elif not path_request_throttle and Transport.hops_to(link_entry[6]) == 1:
|
||||
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and destination was previously local to an interface on this instance", RNS.LOG_DEBUG)
|
||||
path_request_conditions = True
|
||||
blocked_if = link_entry[4]
|
||||
|
||||
# If the link destination was previously only 1 hop
|
||||
# away, this likely means that it was local to one
|
||||
# of our interfaces, and that it roamed somewhere else.
|
||||
# In that case, try to discover a new path.
|
||||
# If the link initiator is only 1 hop away,
|
||||
# this likely means that network topology has
|
||||
# changed. In that case, we try to discover a new path,
|
||||
# and mark the old one as potentially unresponsive.
|
||||
elif not path_request_throttle and lr_taken_hops == 1:
|
||||
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and link initiator is local to an interface on this instance", RNS.LOG_DEBUG)
|
||||
path_request_conditions = True
|
||||
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 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():
|
||||
# Drop current path if we are not a transport instance, to
|
||||
@@ -543,8 +571,6 @@ class Transport:
|
||||
else:
|
||||
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME)
|
||||
|
||||
|
||||
|
||||
i = 0
|
||||
for truncated_packet_hash in stale_reverse_entries:
|
||||
Transport.reverse_table.pop(truncated_packet_hash)
|
||||
@@ -556,8 +582,6 @@ class Transport:
|
||||
else:
|
||||
RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME)
|
||||
|
||||
|
||||
|
||||
i = 0
|
||||
for link_id in stale_links:
|
||||
Transport.link_table.pop(link_id)
|
||||
@@ -602,8 +626,24 @@ class Transport:
|
||||
else:
|
||||
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()
|
||||
|
||||
if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
|
||||
for interface in Transport.interfaces:
|
||||
interface.process_held_announces()
|
||||
Transport.interface_last_jobs = time.time()
|
||||
|
||||
else:
|
||||
# Transport jobs were locked, do nothing
|
||||
pass
|
||||
@@ -618,7 +658,17 @@ class Transport:
|
||||
packet.send()
|
||||
|
||||
for destination_hash in path_requests:
|
||||
Transport.request_path(destination_hash)
|
||||
blocked_if = path_requests[destination_hash]
|
||||
if blocked_if == None:
|
||||
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
|
||||
def transmit(interface, raw):
|
||||
@@ -675,6 +725,29 @@ class Transport:
|
||||
sent = False
|
||||
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)
|
||||
|
||||
Transport.cache(packet)
|
||||
|
||||
# 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:
|
||||
outbound_interface = Transport.destination_table[packet.destination_hash][5]
|
||||
@@ -692,6 +765,7 @@ class Transport:
|
||||
new_raw += packet.raw[1:2]
|
||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||
new_raw += packet.raw[2:]
|
||||
packet_sent(packet)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
sent = True
|
||||
@@ -711,6 +785,7 @@ class Transport:
|
||||
new_raw += packet.raw[1:2]
|
||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||
new_raw += packet.raw[2:]
|
||||
packet_sent(packet)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
sent = True
|
||||
@@ -719,6 +794,7 @@ class Transport:
|
||||
# directly reachable, and also on which interface, so we
|
||||
# simply transmit the packet directly on that one.
|
||||
else:
|
||||
packet_sent(packet)
|
||||
Transport.transmit(outbound_interface, packet.raw)
|
||||
sent = True
|
||||
|
||||
@@ -802,7 +878,7 @@ class Transport:
|
||||
interface.announce_queue = []
|
||||
|
||||
queued_announces = True if len(interface.announce_queue) > 0 else False
|
||||
if not queued_announces and outbound_time > interface.announce_allowed_at:
|
||||
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
|
||||
wait_time = (tx_time / interface.announce_cap)
|
||||
interface.announce_allowed_at = outbound_time + wait_time
|
||||
@@ -883,28 +959,11 @@ class Transport:
|
||||
# thread.start()
|
||||
|
||||
Transport.transmit(interface, packet.raw)
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
interface.sent_announce()
|
||||
packet_sent(packet)
|
||||
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
|
||||
return sent
|
||||
|
||||
@@ -1036,6 +1095,7 @@ class Transport:
|
||||
|
||||
packet = RNS.Packet(None, raw)
|
||||
if not packet.unpack():
|
||||
Transport.jobs_locked = False
|
||||
return
|
||||
|
||||
packet.receiving_interface = interface
|
||||
@@ -1049,7 +1109,7 @@ class Transport:
|
||||
Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi])
|
||||
|
||||
while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
||||
Transport.local_client_rssi_cache.pop()
|
||||
Transport.local_client_rssi_cache.pop(0)
|
||||
|
||||
if hasattr(interface, "r_stat_snr"):
|
||||
if interface.r_stat_rssi != None:
|
||||
@@ -1058,7 +1118,16 @@ class Transport:
|
||||
Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr])
|
||||
|
||||
while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
|
||||
Transport.local_client_snr_cache.pop()
|
||||
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 Transport.is_local_client_interface(interface):
|
||||
@@ -1117,6 +1186,7 @@ class Transport:
|
||||
# normal processing.
|
||||
if packet.context == RNS.Packet.CACHE_REQUEST:
|
||||
if Transport.cache_request_packet(packet):
|
||||
Transport.jobs_locked = False
|
||||
return
|
||||
|
||||
# If the packet is in transport, check whether we
|
||||
@@ -1150,7 +1220,8 @@ class Transport:
|
||||
|
||||
if packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||
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
|
||||
link_entry = [ now, # 0: Timestamp,
|
||||
@@ -1223,8 +1294,21 @@ class Transport:
|
||||
# announces, queueing rebroadcasts of these, and removal
|
||||
# of queued announce rebroadcasts once handed to the next node.
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
if interface != None and RNS.Identity.validate_announce(packet, only_validate_signature=True):
|
||||
interface.received_announce()
|
||||
|
||||
if not packet.destination_hash in Transport.destination_table:
|
||||
# This is an unknown destination, and we'll apply
|
||||
# potential ingress limiting. Already known
|
||||
# destinations will have re-announces controlled
|
||||
# by normal announce rate limiting.
|
||||
if interface.should_ingress_limit():
|
||||
interface.hold_announce(packet)
|
||||
Transport.jobs_locked = False
|
||||
return
|
||||
|
||||
local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None)
|
||||
if local_destination == None and RNS.Identity.validate_announce(packet):
|
||||
if local_destination == None and RNS.Identity.validate_announce(packet):
|
||||
if packet.transport_id != None:
|
||||
received_from = packet.transport_id
|
||||
|
||||
@@ -1239,7 +1323,8 @@ class Transport:
|
||||
announce_entry[6] += 1
|
||||
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)
|
||||
Transport.announce_table.pop(packet.destination_hash)
|
||||
if packet.destination_hash in Transport.announce_table:
|
||||
Transport.announce_table.pop(packet.destination_hash)
|
||||
|
||||
if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0:
|
||||
now = time.time()
|
||||
@@ -1274,6 +1359,7 @@ class Transport:
|
||||
# TODO: Check whether this approach works
|
||||
# under all circumstances
|
||||
if not random_blob in random_blobs:
|
||||
Transport.mark_path_unknown_state(packet.destination_hash)
|
||||
should_add = True
|
||||
else:
|
||||
should_add = False
|
||||
@@ -1291,21 +1377,39 @@ class Transport:
|
||||
if path_announce_emitted >= announce_emitted:
|
||||
break
|
||||
|
||||
# If the path has expired, consider this
|
||||
# announce for adding to the path table.
|
||||
if (now >= path_expires):
|
||||
# We also check that the announce is
|
||||
# We check that the announce is
|
||||
# different from ones we've already heard,
|
||||
# to avoid loops in the network
|
||||
if not random_blob in random_blobs:
|
||||
# TODO: Check that this ^ approach actually
|
||||
# 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)
|
||||
Transport.mark_path_unknown_state(packet.destination_hash)
|
||||
should_add = True
|
||||
else:
|
||||
should_add = False
|
||||
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 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)
|
||||
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
|
||||
else:
|
||||
should_add = False
|
||||
@@ -1316,7 +1420,7 @@ class Transport:
|
||||
should_add = True
|
||||
|
||||
if should_add:
|
||||
now = time.time()
|
||||
now = time.time()
|
||||
|
||||
rate_blocked = False
|
||||
if packet.context != RNS.Packet.PATH_RESPONSE and packet.receiving_interface.announce_rate_target != None:
|
||||
@@ -1369,6 +1473,7 @@ class Transport:
|
||||
expires = now + Transport.PATHFINDER_E
|
||||
|
||||
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:
|
||||
# Insert announce into announce table for retransmission
|
||||
@@ -1573,32 +1678,35 @@ class Transport:
|
||||
# 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:
|
||||
link_entry = Transport.link_table[packet.destination_hash]
|
||||
if packet.receiving_interface == link_entry[2]:
|
||||
try:
|
||||
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
|
||||
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
|
||||
peer_identity = RNS.Identity.recall(link_entry[6])
|
||||
peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
|
||||
if packet.hops == link_entry[3]:
|
||||
if packet.receiving_interface == link_entry[2]:
|
||||
try:
|
||||
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
|
||||
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
|
||||
peer_identity = RNS.Identity.recall(link_entry[6])
|
||||
peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
|
||||
|
||||
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
|
||||
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
|
||||
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
|
||||
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
|
||||
|
||||
if peer_identity.validate(signature, signed_data):
|
||||
RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
Transport.link_table[packet.destination_hash][7] = True
|
||||
Transport.transmit(link_entry[4], new_raw)
|
||||
if peer_identity.validate(signature, signed_data):
|
||||
RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
Transport.link_table[packet.destination_hash][7] = True
|
||||
Transport.transmit(link_entry[4], new_raw)
|
||||
|
||||
else:
|
||||
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
except Exception as e:
|
||||
RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
else:
|
||||
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
|
||||
RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG)
|
||||
else:
|
||||
# Check if we can deliver it to a local
|
||||
# pending link
|
||||
@@ -1640,9 +1748,6 @@ class Transport:
|
||||
if receipt.hash == proof_hash:
|
||||
receipt_validated = receipt.validate_proof_packet(packet)
|
||||
else:
|
||||
# TODO: This looks like it should actually
|
||||
# be rewritten when implicit proofs are added.
|
||||
|
||||
# In case of an implicit proof, we have
|
||||
# to check every single outstanding receipt
|
||||
receipt_validated = receipt.validate_proof_packet(packet)
|
||||
@@ -1947,6 +2052,45 @@ class Transport:
|
||||
else:
|
||||
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
|
||||
def expire_path(destination_hash):
|
||||
if destination_hash in Transport.destination_table:
|
||||
@@ -1956,6 +2100,37 @@ class Transport:
|
||||
else:
|
||||
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
|
||||
|
||||
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
|
||||
def request_path(destination_hash, on_interface=None, tag=None, recursive=False):
|
||||
"""
|
||||
@@ -2210,7 +2385,10 @@ class Transport:
|
||||
pass
|
||||
|
||||
for interface in detachable_interfaces:
|
||||
interface.detach()
|
||||
try:
|
||||
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
|
||||
def shared_connection_disappeared():
|
||||
@@ -2408,7 +2586,7 @@ class Transport:
|
||||
received_from = de[1]
|
||||
hops = de[2]
|
||||
expires = de[3]
|
||||
random_blobs = de[4]
|
||||
random_blobs = de[4][-Transport.PERSIST_RANDOM_BLOBS:]
|
||||
packet_hash = de[6].get_hash()
|
||||
|
||||
serialised_entry = [
|
||||
|
||||
@@ -321,7 +321,7 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
|
||||
if silent:
|
||||
print("Establishing link with "+RNS.prettyhexrep(destination_hash))
|
||||
else:
|
||||
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
|
||||
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
|
||||
|
||||
listener_identity = RNS.Identity.recall(destination_hash)
|
||||
listener_destination = RNS.Destination(
|
||||
|
||||
+90
-9
@@ -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("-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("-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("-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("-d", "--decrypt", metavar="path", action="store", default=None, help="decrypt file")
|
||||
parser.add_argument("-e", "--encrypt", metavar="file", action="store", default=None, help="encrypt 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("-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("-w", "--write", metavar="path", action="store", default=None, help="output 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="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("-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",
|
||||
@@ -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-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__))
|
||||
|
||||
@@ -110,6 +114,59 @@ def main():
|
||||
args.read = args.sign
|
||||
|
||||
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:
|
||||
print("\nNo identity provided, cannot continue\n")
|
||||
parser.print_help()
|
||||
@@ -196,7 +253,7 @@ def main():
|
||||
if args.hash:
|
||||
try:
|
||||
aspects = args.hash.split(".")
|
||||
if not len(aspects) > 1:
|
||||
if not len(aspects) > 0:
|
||||
RNS.log("Invalid destination aspects specified", RNS.LOG_ERROR)
|
||||
exit(32)
|
||||
else:
|
||||
@@ -246,14 +303,38 @@ def main():
|
||||
exit(0)
|
||||
|
||||
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 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:
|
||||
RNS.log("Private Key : Hidden")
|
||||
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 not args.read and args.validate.lower().endswith("."+SIG_EXT):
|
||||
args.read = str(args.validate).replace("."+SIG_EXT, "")
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2023 Mark Qvist / unsigned.io
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import RNS
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from RNS._version import __version__
|
||||
|
||||
|
||||
def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
|
||||
targetverbosity = verbosity-quietness
|
||||
|
||||
if service:
|
||||
targetlogdest = RNS.LOG_FILE
|
||||
targetverbosity = None
|
||||
else:
|
||||
targetlogdest = RNS.LOG_STDOUT
|
||||
|
||||
reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
|
||||
exit(0)
|
||||
|
||||
def main():
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description="Reticulum Distributed Identity Resolver")
|
||||
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||
parser.add_argument('-q', '--quiet', action='count', default=0)
|
||||
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
||||
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.exampleconfig:
|
||||
print(__example_rns_config__)
|
||||
exit()
|
||||
|
||||
if args.config:
|
||||
configarg = args.config
|
||||
else:
|
||||
configarg = None
|
||||
|
||||
program_setup(configdir = configarg, verbosity=args.verbose, quietness=args.quiet)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("")
|
||||
exit()
|
||||
|
||||
__example_rns_config__ = '''# This is an example Identity Resolver file.
|
||||
'''
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+249
-82
@@ -42,8 +42,8 @@ RNS.logtimefmt = "%H:%M:%S"
|
||||
RNS.compact_log_fmt = True
|
||||
|
||||
program_version = "2.1.3"
|
||||
eth_addr = "0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a"
|
||||
btc_addr = "3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq"
|
||||
eth_addr = "0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73"
|
||||
btc_addr = "35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH"
|
||||
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
|
||||
|
||||
rnode = None
|
||||
@@ -120,12 +120,16 @@ class KISS():
|
||||
class ROM():
|
||||
PLATFORM_AVR = 0x90
|
||||
PLATFORM_ESP32 = 0x80
|
||||
PLATFORM_NRF52 = 0x70
|
||||
|
||||
MCU_1284P = 0x91
|
||||
MCU_2560 = 0x92
|
||||
MCU_ESP32 = 0x81
|
||||
MCU_NRF52 = 0x71
|
||||
|
||||
PRODUCT_RNODE = 0x03
|
||||
MODEL_A1 = 0xA1
|
||||
MODEL_A6 = 0xA6
|
||||
MODEL_A4 = 0xA4
|
||||
MODEL_A9 = 0xA9
|
||||
MODEL_A3 = 0xA3
|
||||
@@ -144,6 +148,8 @@ class ROM():
|
||||
PRODUCT_T32_21 = 0xB1
|
||||
MODEL_B4 = 0xB4
|
||||
MODEL_B9 = 0xB9
|
||||
MODEL_B4_TCXO = 0x04
|
||||
MODEL_B9_TCXO = 0x09
|
||||
|
||||
PRODUCT_H32_V2 = 0xC0
|
||||
MODEL_C4 = 0xC4
|
||||
@@ -152,6 +158,8 @@ class ROM():
|
||||
PRODUCT_TBEAM = 0xE0
|
||||
MODEL_E4 = 0xE4
|
||||
MODEL_E9 = 0xE9
|
||||
MODEL_E3 = 0xE3
|
||||
MODEL_E8 = 0xE8
|
||||
|
||||
PRODUCT_HMBRW = 0xF0
|
||||
MODEL_FF = 0xFF
|
||||
@@ -182,6 +190,7 @@ class ROM():
|
||||
BOARD_GENERIC_ESP32 = 0x35
|
||||
BOARD_LORA32_V2_0 = 0x36
|
||||
BOARD_LORA32_V2_1 = 0x37
|
||||
BOARD_RAK4630 = 0x51
|
||||
|
||||
mapped_product = ROM.PRODUCT_RNODE
|
||||
products = {
|
||||
@@ -197,33 +206,41 @@ products = {
|
||||
platforms = {
|
||||
ROM.PLATFORM_AVR: "AVR",
|
||||
ROM.PLATFORM_ESP32:"ESP32",
|
||||
ROM.PLATFORM_NRF52:"NRF52",
|
||||
}
|
||||
|
||||
mcus = {
|
||||
ROM.MCU_1284P: "ATmega1284P",
|
||||
ROM.MCU_2560:"ATmega2560",
|
||||
ROM.MCU_ESP32:"Espressif Systems ESP32",
|
||||
ROM.MCU_NRF52:"Nordic nRF52840",
|
||||
}
|
||||
|
||||
models = {
|
||||
0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex"],
|
||||
0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex"],
|
||||
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip"],
|
||||
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip"],
|
||||
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip"],
|
||||
0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip"],
|
||||
0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip"],
|
||||
0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip"],
|
||||
0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip"],
|
||||
0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip"],
|
||||
0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip"],
|
||||
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip"],
|
||||
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip"],
|
||||
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip"],
|
||||
0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip"],
|
||||
0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip"],
|
||||
0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None],
|
||||
0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None],
|
||||
0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex", "SX1278"],
|
||||
0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex", "SX1276"],
|
||||
0xA1: [410000000, 525000000, 22, "410 - 525 MHz", "rnode_firmware_t3s3.zip", "SX1268"],
|
||||
0xA6: [820000000, 1020000000, 22, "820 - 960 MHz", "rnode_firmware_t3s3.zip", "SX1262"],
|
||||
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip", "SX1278"],
|
||||
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip", "SX1276"],
|
||||
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip", "SX1278"],
|
||||
0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip", "SX1276"],
|
||||
0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip", "SX1278"],
|
||||
0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip", "SX1276"],
|
||||
0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip", "SX1278"],
|
||||
0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip", "SX1276"],
|
||||
0x04: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1278"],
|
||||
0x09: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1276"],
|
||||
0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip", "SX1278"],
|
||||
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip", "SX1276"],
|
||||
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip", "SX1278"],
|
||||
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip", "SX1276"],
|
||||
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
|
||||
@@ -516,7 +533,7 @@ class RNode():
|
||||
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])
|
||||
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):
|
||||
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
|
||||
elif (command == KISS.CMD_RANDOM):
|
||||
@@ -928,6 +945,7 @@ class RNode():
|
||||
except Exception as e:
|
||||
self.provisioned = False
|
||||
RNS.log("Invalid EEPROM data, could not parse device EEPROM.")
|
||||
RNS.log("The contained exception was: "+str(e))
|
||||
|
||||
|
||||
def device_probe(self):
|
||||
@@ -1018,6 +1036,10 @@ def ensure_firmware_file(fw_filename):
|
||||
file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb")
|
||||
release_info = file.read().decode("utf-8").strip()
|
||||
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]
|
||||
if not os.path.isdir(UPD_DIR+"/"+selected_version):
|
||||
os.makedirs(UPD_DIR+"/"+selected_version)
|
||||
@@ -1169,15 +1191,15 @@ def main():
|
||||
|
||||
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("-r", "--rom", action="store_true", help=argparse.SUPPRESS) # 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("-S", "--sign", action="store_true", help=argparse.SUPPRESS) # 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("--platform", action="store", metavar="platform", type=str, default=None, help=argparse.SUPPRESS) # 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("--model", action="store", metavar="model", type=str, default=None, help=argparse.SUPPRESS) # 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("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM")
|
||||
parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
|
||||
parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") #
|
||||
parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
|
||||
parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash")
|
||||
parser.add_argument("--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="Product specification 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="Hardware revision for device bootstrap")
|
||||
|
||||
parser.add_argument("port", nargs="?", default=None, help="serial port where RNode is attached", type=str)
|
||||
args = parser.parse_args()
|
||||
@@ -1497,6 +1519,7 @@ def main():
|
||||
print("[5] LilyGO LoRa32 v1.0")
|
||||
print("[6] LilyGO T-Beam")
|
||||
print("[7] Heltec LoRa32 v2")
|
||||
print("[8] LilyGO LoRa T3S3")
|
||||
print(" .")
|
||||
print(" / \\ Select one of these options if you want to easily turn")
|
||||
print(" | a supported development board into an RNode.")
|
||||
@@ -1507,7 +1530,8 @@ def main():
|
||||
selected_product = None
|
||||
try:
|
||||
c_dev = int(input())
|
||||
if c_dev < 1 or c_dev > 7:
|
||||
c_mod = False
|
||||
if c_dev < 1 or c_dev > 8:
|
||||
raise ValueError()
|
||||
elif c_dev == 1:
|
||||
selected_product = ROM.PRODUCT_RNODE
|
||||
@@ -1536,8 +1560,7 @@ def main():
|
||||
print(" T-Beam RNode Installer")
|
||||
print("")
|
||||
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("SX1262 and SX1268 chips is in development.")
|
||||
print("SX1276, SX1278, SX1262 and SX1268 transceiver chips.")
|
||||
print("")
|
||||
print("Important! Using RNode firmware on T-Beam devices should currently be")
|
||||
print("considered experimental. It is not intended for production or critical use.")
|
||||
@@ -1608,6 +1631,23 @@ def main():
|
||||
print("who would like to experiment with it. Hit enter to continue.")
|
||||
print("---------------------------------------------------------------------------")
|
||||
input()
|
||||
elif c_dev == 8:
|
||||
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()
|
||||
except Exception as e:
|
||||
print("That device type does not exist, exiting now.")
|
||||
exit()
|
||||
@@ -1660,53 +1700,86 @@ def main():
|
||||
|
||||
|
||||
elif selected_product == ROM.PRODUCT_RNODE:
|
||||
selected_mcu = ROM.MCU_1284P
|
||||
print("\nWhat model is this RNode?\n")
|
||||
print("[1] Handheld v2.x RNode, 410 - 525 MHz")
|
||||
print("[2] Handheld v2.x RNode, 820 - 1020 MHz")
|
||||
print("")
|
||||
print("[3] Original v1.x RNode, 410 - 525 MHz")
|
||||
print("[4] Original v1.x RNode, 820 - 1020 MHz")
|
||||
# print("[5] Prototype v2 RNode, 410 - 525 MHz")
|
||||
# print("[6] Prototype v2 RNode, 820 - 1020 MHz")
|
||||
print("\n? ", end="")
|
||||
try:
|
||||
c_model = int(input())
|
||||
if c_model < 1 or c_model > 6:
|
||||
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:
|
||||
selected_model = ROM.MODEL_A2
|
||||
selected_mcu = ROM.MCU_ESP32
|
||||
selected_platform = ROM.PLATFORM_ESP32
|
||||
elif c_model == 2:
|
||||
selected_model = ROM.MODEL_A7
|
||||
selected_mcu = ROM.MCU_ESP32
|
||||
selected_platform = ROM.PLATFORM_ESP32
|
||||
# elif c_model == 5:
|
||||
# selected_model = ROM.MODEL_A3
|
||||
# selected_mcu = ROM.MCU_ESP32
|
||||
# selected_platform = ROM.PLATFORM_ESP32
|
||||
# elif c_model == 6:
|
||||
# selected_model = ROM.MODEL_A8
|
||||
# selected_mcu = ROM.MCU_ESP32
|
||||
# selected_platform = ROM.PLATFORM_ESP32
|
||||
except Exception as e:
|
||||
print("That model does not exist, exiting now.")
|
||||
exit()
|
||||
if not c_mod:
|
||||
selected_mcu = ROM.MCU_1284P
|
||||
print("\nWhat model is this RNode?\n")
|
||||
print("[1] Handheld v2.1 RNode, 410 - 525 MHz")
|
||||
print("[2] Handheld v2.1 RNode, 820 - 1020 MHz")
|
||||
print("")
|
||||
print("[3] Original v1.x RNode, 410 - 525 MHz")
|
||||
print("[4] Original v1.x RNode, 820 - 1020 MHz")
|
||||
print("")
|
||||
print("[5] Prototype v2.2 RNode, 410 - 525 MHz")
|
||||
print("[6] Prototype v2.2 RNode, 820 - 1020 MHz")
|
||||
# print("[5] Prototype v2 RNode, 410 - 525 MHz")
|
||||
# print("[6] Prototype v2 RNode, 820 - 1020 MHz")
|
||||
print("\n? ", end="")
|
||||
try:
|
||||
c_model = int(input())
|
||||
if c_model < 1 or c_model > 6:
|
||||
raise ValueError()
|
||||
elif c_model == 1:
|
||||
selected_model = ROM.MODEL_A2
|
||||
selected_mcu = ROM.MCU_ESP32
|
||||
selected_platform = ROM.PLATFORM_ESP32
|
||||
elif c_model == 2:
|
||||
selected_model = ROM.MODEL_A7
|
||||
selected_mcu = ROM.MCU_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:
|
||||
# selected_model = ROM.MODEL_A3
|
||||
# selected_mcu = ROM.MCU_ESP32
|
||||
# selected_platform = ROM.PLATFORM_ESP32
|
||||
# elif c_model == 6:
|
||||
# selected_model = ROM.MODEL_A8
|
||||
# selected_mcu = ROM.MCU_ESP32
|
||||
# selected_platform = ROM.PLATFORM_ESP32
|
||||
except Exception as e:
|
||||
print("That model does not exist, exiting now.")
|
||||
exit()
|
||||
else:
|
||||
print("\nWhat model is this T3S3?\n")
|
||||
print("[1] 410 - 525 MHz (with SX1268 chip)")
|
||||
print("[2] 820 - 1020 MHz (with SX1268 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:
|
||||
selected_mcu = ROM.MCU_ESP32
|
||||
print("\nWhat band is this T-Beam for?\n")
|
||||
print("[1] 433 MHz")
|
||||
print("[2] 868 MHz")
|
||||
print("[3] 915 MHz")
|
||||
print("[4] 923 MHz")
|
||||
print("[1] 433 MHz (with SX1278 chip)")
|
||||
print("[2] 868/915/923 MHz (with SX1276 chip)")
|
||||
print("");
|
||||
print("[3] 433 MHz (with SX1268 chip)")
|
||||
print("[4] 868/915/923 MHz (with SX1262 chip)")
|
||||
print("\n? ", end="")
|
||||
try:
|
||||
c_model = int(input())
|
||||
@@ -1715,9 +1788,15 @@ def main():
|
||||
elif c_model == 1:
|
||||
selected_model = ROM.MODEL_E4
|
||||
selected_platform = ROM.PLATFORM_ESP32
|
||||
elif c_model > 1:
|
||||
elif c_model == 2:
|
||||
selected_model = ROM.MODEL_E9
|
||||
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:
|
||||
print("That band does not exist, exiting now.")
|
||||
exit()
|
||||
@@ -1770,9 +1849,9 @@ def main():
|
||||
selected_mcu = ROM.MCU_ESP32
|
||||
print("\nWhat band is this LoRa32 for?\n")
|
||||
print("[1] 433 MHz")
|
||||
print("[2] 868 MHz")
|
||||
print("[3] 915 MHz")
|
||||
print("[4] 923 MHz")
|
||||
print("[2] 868/915/923 MHz")
|
||||
print("[3] 433 MHz, with TCXO")
|
||||
print("[4] 868/915/923 MHz, with TCXO")
|
||||
print("\n? ", end="")
|
||||
try:
|
||||
c_model = int(input())
|
||||
@@ -1781,9 +1860,15 @@ def main():
|
||||
elif c_model == 1:
|
||||
selected_model = ROM.MODEL_B4
|
||||
selected_platform = ROM.PLATFORM_ESP32
|
||||
elif c_model > 1:
|
||||
elif c_model == 2:
|
||||
selected_model = ROM.MODEL_B9
|
||||
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:
|
||||
print("That band does not exist, exiting now.")
|
||||
exit()
|
||||
@@ -2098,6 +2183,42 @@ def main():
|
||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
|
||||
"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":
|
||||
if numeric_version >= 1.59:
|
||||
return [
|
||||
@@ -2206,6 +2327,24 @@ def main():
|
||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin",
|
||||
"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":
|
||||
if numeric_version >= 1.55:
|
||||
return [
|
||||
@@ -2386,6 +2525,24 @@ def main():
|
||||
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin",
|
||||
"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",
|
||||
"0x1000", 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":
|
||||
return [
|
||||
sys.executable, flasher,
|
||||
@@ -2770,6 +2927,7 @@ def main():
|
||||
RNS.log("\tFirmware version : "+rnode.version)
|
||||
RNS.log("\tHardware revision : "+str(int(rnode.hw_rev)))
|
||||
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("\tMax TX power : "+str(rnode.max_output)+" dBm")
|
||||
RNS.log("\tManufactured : "+timestring)
|
||||
@@ -2851,12 +3009,21 @@ def main():
|
||||
mapped_product = ROM.PRODUCT_TBEAM
|
||||
|
||||
if mapped_model != None:
|
||||
model = mapped_model
|
||||
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
|
||||
else:
|
||||
if args.model == "a4":
|
||||
model = ROM.MODEL_A4
|
||||
elif args.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":
|
||||
model = ROM.MODEL_E4
|
||||
elif args.model == "e9":
|
||||
|
||||
+33
-2
@@ -30,7 +30,7 @@ import argparse
|
||||
from RNS._version import __version__
|
||||
|
||||
|
||||
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues):
|
||||
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues, drop_via):
|
||||
if table:
|
||||
destination_hash = None
|
||||
if destination_hexhash != None:
|
||||
@@ -155,6 +155,29 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
elif drop_via:
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(destination_hexhash) != dest_len:
|
||||
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(destination_hexhash)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
if reticulum.drop_all_via(destination_hash):
|
||||
print("Dropped all paths via "+RNS.prettyhexrep(destination_hash))
|
||||
else:
|
||||
print("Unable to drop paths via "+RNS.prettyhexrep(destination_hash)+". Does the transport instance exist?")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
else:
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
@@ -256,6 +279,13 @@ def main():
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-x", "--drop-via",
|
||||
action="store_true",
|
||||
help="drop all paths via specified transport instance",
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
action="store",
|
||||
@@ -282,7 +312,7 @@ def main():
|
||||
else:
|
||||
configarg = None
|
||||
|
||||
if not args.drop_announces and not args.table and not args.rates and not args.destination:
|
||||
if not args.drop_announces and not args.table and not args.rates and not args.destination and not args.drop_via:
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
@@ -296,6 +326,7 @@ def main():
|
||||
verbosity = args.verbose,
|
||||
timeout = args.w,
|
||||
drop_queues = args.drop_announces,
|
||||
drop_via = args.drop_via,
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
+105
-63
@@ -31,8 +31,9 @@ import argparse
|
||||
from RNS._version import __version__
|
||||
|
||||
DEFAULT_PROBE_SIZE = 16
|
||||
DEFAULT_TIMEOUT = 12
|
||||
|
||||
def program_setup(configdir, destination_hexhash, size=None, full_name = None, verbosity = 0):
|
||||
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 full_name == None:
|
||||
print("The full destination name including application name aspects must be specified for the destination")
|
||||
@@ -72,14 +73,19 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||
sys.stdout.flush()
|
||||
|
||||
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
|
||||
i = 0
|
||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||
while not RNS.Transport.has_path(destination_hash):
|
||||
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
if time.time() > _timeout:
|
||||
print("\r \rPath request timed out")
|
||||
exit(1)
|
||||
|
||||
server_identity = RNS.Identity.recall(destination_hash)
|
||||
|
||||
request_destination = RNS.Destination(
|
||||
@@ -90,76 +96,106 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||
*aspects
|
||||
)
|
||||
|
||||
try:
|
||||
probe = RNS.Packet(request_destination, os.urandom(size))
|
||||
probe.pack()
|
||||
except OSError:
|
||||
print("Error: Probe packet size of "+str(len(probe.raw))+" bytes exceed MTU of "+str(RNS.Reticulum.MTU)+" bytes")
|
||||
exit(1)
|
||||
sent = 0
|
||||
replies = 0
|
||||
while probes:
|
||||
|
||||
receipt = probe.send()
|
||||
if sent > 0:
|
||||
time.sleep(wait)
|
||||
|
||||
if more_output:
|
||||
nhd = reticulum.get_next_hop(destination_hash)
|
||||
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else ""
|
||||
if_str = " on "+str(reticulum.get_next_hop_if_name(destination_hash)) if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
|
||||
more = via_str+if_str
|
||||
else:
|
||||
more = ""
|
||||
try:
|
||||
probe = RNS.Packet(request_destination, os.urandom(size))
|
||||
probe.pack()
|
||||
except OSError:
|
||||
print("Error: Probe packet size of "+str(len(probe.raw))+" bytes exceed MTU of "+str(RNS.Reticulum.MTU)+" bytes")
|
||||
exit(3)
|
||||
|
||||
print("\rSent "+str(size)+" byte probe to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
|
||||
receipt = probe.send()
|
||||
sent += 1
|
||||
|
||||
i = 0
|
||||
while not receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
if more_output:
|
||||
nhd = reticulum.get_next_hop(destination_hash)
|
||||
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else ""
|
||||
if_str = " on "+str(reticulum.get_next_hop_if_name(destination_hash)) if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
|
||||
more = via_str+if_str
|
||||
else:
|
||||
more = ""
|
||||
|
||||
print("\b\b ")
|
||||
sys.stdout.flush()
|
||||
print("\rSent probe "+str(sent)+" ("+str(size)+" bytes) to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
|
||||
|
||||
hops = RNS.Transport.hops_to(destination_hash)
|
||||
if hops != 1:
|
||||
ms = "s"
|
||||
else:
|
||||
ms = ""
|
||||
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
|
||||
i = 0
|
||||
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
rtt = receipt.get_rtt()
|
||||
if (rtt >= 1):
|
||||
rtt = round(rtt, 3)
|
||||
rttstring = str(rtt)+" seconds"
|
||||
else:
|
||||
rtt = round(rtt*1000, 3)
|
||||
rttstring = str(rtt)+" milliseconds"
|
||||
|
||||
reception_stats = ""
|
||||
if reticulum.is_connected_to_shared_instance:
|
||||
reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash)
|
||||
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
|
||||
|
||||
if reception_rssi != None:
|
||||
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
|
||||
if time.time() > _timeout:
|
||||
print("\r \rProbe timed out")
|
||||
|
||||
if reception_snr != None:
|
||||
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
||||
else:
|
||||
print("\b\b ")
|
||||
sys.stdout.flush()
|
||||
|
||||
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||
replies += 1
|
||||
hops = RNS.Transport.hops_to(destination_hash)
|
||||
if hops != 1:
|
||||
ms = "s"
|
||||
else:
|
||||
ms = ""
|
||||
|
||||
rtt = receipt.get_rtt()
|
||||
if (rtt >= 1):
|
||||
rtt = round(rtt, 3)
|
||||
rttstring = str(rtt)+" seconds"
|
||||
else:
|
||||
rtt = round(rtt*1000, 3)
|
||||
rttstring = str(rtt)+" milliseconds"
|
||||
|
||||
reception_stats = ""
|
||||
if reticulum.is_connected_to_shared_instance:
|
||||
reception_rssi = reticulum.get_packet_rssi(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:
|
||||
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
|
||||
|
||||
if reception_snr != None:
|
||||
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
||||
|
||||
if reception_q != None:
|
||||
reception_stats += " [Link Quality "+str(reception_q)+"%]"
|
||||
|
||||
else:
|
||||
if receipt.proof_packet != None:
|
||||
if receipt.proof_packet.rssi != None:
|
||||
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
|
||||
|
||||
if receipt.proof_packet.snr != None:
|
||||
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
||||
|
||||
print(
|
||||
"Valid reply from "+
|
||||
RNS.prettyhexrep(receipt.destination.hash)+
|
||||
"\nRound-trip time is "+rttstring+
|
||||
" over "+str(hops)+" hop"+ms+
|
||||
reception_stats+"\n"
|
||||
)
|
||||
|
||||
else:
|
||||
print("\r \rProbe timed out")
|
||||
|
||||
probes -= 1
|
||||
|
||||
loss = round((1-(replies/sent))*100, 2)
|
||||
print(f"Sent {sent}, received {replies}, packet loss {loss}%")
|
||||
if loss > 0:
|
||||
exit(2)
|
||||
else:
|
||||
if receipt.proof_packet != None:
|
||||
if receipt.proof_packet.rssi != None:
|
||||
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
|
||||
|
||||
if receipt.proof_packet.snr != None:
|
||||
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
||||
|
||||
print(
|
||||
"Valid reply received from "+
|
||||
RNS.prettyhexrep(receipt.destination.hash)+
|
||||
"\nRound-trip time is "+rttstring+
|
||||
" over "+str(hops)+" hop"+ms+
|
||||
reception_stats
|
||||
)
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -168,6 +204,9 @@ def main():
|
||||
|
||||
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("-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("-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("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)
|
||||
@@ -191,7 +230,10 @@ def main():
|
||||
destination_hexhash = args.destination_hash,
|
||||
size = args.size,
|
||||
full_name = args.full_name,
|
||||
verbosity = args.verbose
|
||||
verbosity = args.verbose,
|
||||
probes = args.probes,
|
||||
wait = args.wait,
|
||||
timeout = args.timeout,
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
|
||||
@@ -112,6 +112,17 @@ shared_instance_port = 37428
|
||||
instance_control_port = 37429
|
||||
|
||||
|
||||
# On systems where running instances may not have access
|
||||
# to the same shared Reticulum configuration directory,
|
||||
# it is still possible to allow full interactivity for
|
||||
# running instances, by manually specifying a shared RPC
|
||||
# key. In almost all cases, this option is not needed, but
|
||||
# it can be useful on operating systems such as Android.
|
||||
# The key must be specified as bytes in hexadecimal.
|
||||
|
||||
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
|
||||
|
||||
|
||||
# You can configure Reticulum to panic and forcibly close
|
||||
# if an unrecoverable interface error occurs, such as the
|
||||
# hardware device for an interface disappearing. This is
|
||||
|
||||
+93
-26
@@ -46,7 +46,7 @@ def size_str(num, suffix='B'):
|
||||
|
||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||
|
||||
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=False):
|
||||
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, sorting=None, sort_reverse=False):
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
stats = None
|
||||
@@ -62,16 +62,38 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
||||
if isinstance(stats[s], bytes):
|
||||
stats[s] = RNS.hexrep(stats[s], delimit=False)
|
||||
|
||||
for i in stats[s]:
|
||||
if isinstance(i, dict):
|
||||
for k in i:
|
||||
if isinstance(i[k], bytes):
|
||||
i[k] = RNS.hexrep(i[k], delimit=False)
|
||||
if isinstance(stats[s], dict):
|
||||
for i in stats[s]:
|
||||
if isinstance(i, dict):
|
||||
for k in i:
|
||||
if isinstance(i[k], bytes):
|
||||
i[k] = RNS.hexrep(i[k], delimit=False)
|
||||
|
||||
print(json.dumps(stats))
|
||||
exit()
|
||||
|
||||
interfaces = stats["interfaces"]
|
||||
if sorting != None and isinstance(sorting, str):
|
||||
sorting = sorting.lower()
|
||||
if sorting == "rate" or sorting == "bitrate":
|
||||
interfaces.sort(key=lambda i: i["bitrate"], reverse=not sort_reverse)
|
||||
if sorting == "rx":
|
||||
interfaces.sort(key=lambda i: i["rxb"], reverse=not sort_reverse)
|
||||
if sorting == "tx":
|
||||
interfaces.sort(key=lambda i: i["txb"], reverse=not sort_reverse)
|
||||
if sorting == "traffic":
|
||||
interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse)
|
||||
if sorting == "announces" or sorting == "announce":
|
||||
interfaces.sort(key=lambda i: i["incoming_announce_frequency"]+i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
||||
if sorting == "arx":
|
||||
interfaces.sort(key=lambda i: i["incoming_announce_frequency"], reverse=not sort_reverse)
|
||||
if sorting == "atx":
|
||||
interfaces.sort(key=lambda i: i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
||||
if sorting == "held":
|
||||
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
|
||||
|
||||
|
||||
for ifstat in stats["interfaces"]:
|
||||
for ifstat in interfaces:
|
||||
name = ifstat["name"]
|
||||
|
||||
if dispall or not (
|
||||
@@ -113,7 +135,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
||||
else:
|
||||
spec_str = " programs"
|
||||
|
||||
clients_string = "Serving : "+str(cnum)+spec_str
|
||||
clients_string = "Serving : "+str(cnum)+spec_str
|
||||
elif name.startswith("I2PInterface["):
|
||||
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
|
||||
cnum = clients
|
||||
@@ -122,11 +144,11 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
||||
else:
|
||||
spec_str = " connected I2P endpoints"
|
||||
|
||||
clients_string = "Peers : "+str(cnum)+spec_str
|
||||
clients_string = "Peers : "+str(cnum)+spec_str
|
||||
else:
|
||||
clients_string = ""
|
||||
else:
|
||||
clients_string = "Clients : "+str(clients)
|
||||
clients_string = "Clients : "+str(clients)
|
||||
|
||||
else:
|
||||
clients = None
|
||||
@@ -134,51 +156,62 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
|
||||
print(" {n}".format(n=ifstat["name"]))
|
||||
|
||||
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
||||
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
||||
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
||||
|
||||
print(" Status : {ss}".format(ss=ss))
|
||||
print(" Status : {ss}".format(ss=ss))
|
||||
|
||||
if clients != None and clients_string != "":
|
||||
print(" "+clients_string)
|
||||
|
||||
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
||||
print(" Mode : {mode}".format(mode=modestr))
|
||||
print(" Mode : {mode}".format(mode=modestr))
|
||||
|
||||
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||
|
||||
if "airtime_short" in ifstat and "airtime_long" in ifstat:
|
||||
print(" Airtime : {atl}% (1h), {ats}% (15s)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
|
||||
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
|
||||
|
||||
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
||||
print(" Ch.Load : {atl}% (1h), {ats}% (15s)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
||||
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
||||
|
||||
if "peers" in ifstat and ifstat["peers"] != None:
|
||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||
|
||||
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
|
||||
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
|
||||
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
|
||||
|
||||
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
||||
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
||||
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
||||
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
||||
|
||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||
|
||||
if "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
||||
if astats and "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
||||
aqn = ifstat["announce_queue"]
|
||||
if aqn == 1:
|
||||
print(" Queued : {np} announce".format(np=aqn))
|
||||
print(" Queued : {np} announce".format(np=aqn))
|
||||
else:
|
||||
print(" Queued : {np} announces".format(np=aqn))
|
||||
print(" Queued : {np} announces".format(np=aqn))
|
||||
|
||||
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
||||
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
|
||||
aqn = ifstat["held_announces"]
|
||||
if aqn == 1:
|
||||
print(" Held : {np} announce".format(np=aqn))
|
||||
else:
|
||||
print(" Held : {np} announces".format(np=aqn))
|
||||
|
||||
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
|
||||
print(" Announces : {iaf}↑".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
|
||||
print(" {iaf}↓".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
|
||||
|
||||
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
||||
|
||||
if "transport_id" in stats and stats["transport_id"] != None:
|
||||
print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
|
||||
if "probe_responder" in stats and stats["probe_responder"] != None:
|
||||
print(" Probe responder at "+RNS.prettyhexrep(stats["probe_responder"]))
|
||||
print(" Probe responder at "+RNS.prettyhexrep(stats["probe_responder"])+ " active")
|
||||
print(" Uptime is "+RNS.prettytime(stats["transport_uptime"]))
|
||||
|
||||
print("")
|
||||
@@ -200,6 +233,31 @@ def main():
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-A",
|
||||
"--announce-stats",
|
||||
action="store_true",
|
||||
help="show announce stats",
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sort",
|
||||
action="store",
|
||||
help="sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]",
|
||||
default=None,
|
||||
type=str
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--reverse",
|
||||
action="store_true",
|
||||
help="reverse sorting",
|
||||
default=False,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--json",
|
||||
@@ -219,7 +277,16 @@ def main():
|
||||
else:
|
||||
configarg = None
|
||||
|
||||
program_setup(configdir = configarg, dispall = args.all, verbosity=args.verbose, name_filter=args.filter, json=args.json)
|
||||
program_setup(
|
||||
configdir = configarg,
|
||||
dispall = args.all,
|
||||
verbosity=args.verbose,
|
||||
name_filter=args.filter,
|
||||
json=args.json,
|
||||
astats=args.announce_stats,
|
||||
sorting=args.sort,
|
||||
sort_reverse=args.reverse,
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("")
|
||||
|
||||
+53
-7
@@ -1,6 +1,6 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
|
||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -38,6 +38,7 @@ from .Transport import Transport
|
||||
from .Destination import Destination
|
||||
from .Packet import Packet
|
||||
from .Packet import PacketReceipt
|
||||
from .Resolver import Resolver
|
||||
from .Resource import Resource, ResourceAdvertisement
|
||||
from .Cryptography import HKDF
|
||||
from .Cryptography import Hashes
|
||||
@@ -143,6 +144,12 @@ def rand():
|
||||
result = instance_random.random()
|
||||
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):
|
||||
try:
|
||||
iter(data)
|
||||
@@ -160,6 +167,9 @@ def prettyhexrep(data):
|
||||
hexrep = "<"+delimiter.join("{:02x}".format(c) for c in data)+">"
|
||||
return hexrep
|
||||
|
||||
def prettyspeed(num, suffix="b"):
|
||||
return prettysize(num/8, suffix=suffix)+"ps"
|
||||
|
||||
def prettysize(num, suffix='B'):
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
@@ -179,32 +189,68 @@ def prettysize(num, suffix='B'):
|
||||
|
||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||
|
||||
def prettytime(time, verbose=False):
|
||||
def prettyfrequency(hz, suffix="Hz"):
|
||||
num = hz*1e6
|
||||
units = ["µ", "m", "", "K","M","G","T","P","E","Z"]
|
||||
last_unit = "Y"
|
||||
|
||||
for unit in units:
|
||||
if abs(num) < 1000.0:
|
||||
return "%.2f %s%s" % (num, unit, suffix)
|
||||
num /= 1000.0
|
||||
|
||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||
|
||||
def 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))
|
||||
time = time % (24 * 3600)
|
||||
hours = int(time // 3600)
|
||||
time %= 3600
|
||||
minutes = int(time // 60)
|
||||
time %= 60
|
||||
seconds = round(time, 2)
|
||||
if compact:
|
||||
seconds = int(time)
|
||||
else:
|
||||
seconds = round(time, 2)
|
||||
|
||||
ss = "" if seconds == 1 else "s"
|
||||
sm = "" if minutes == 1 else "s"
|
||||
sh = "" if hours == 1 else "s"
|
||||
sd = "" if days == 1 else "s"
|
||||
|
||||
displayed = 0
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
displayed += 1
|
||||
|
||||
i = 0
|
||||
tstr = ""
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "0.6.0"
|
||||
__version__ = "0.7.2"
|
||||
|
||||
+10
-10
@@ -14,14 +14,14 @@ This document outlines the currently established development roadmap for Reticul
|
||||
## 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.
|
||||
|
||||
- The current `0.5.x` release cycle aims at completing
|
||||
- [x] Reach feature-completion of the Reticulum API
|
||||
- [x] Improve performance and efficiency of the `Buffer` and `Channel` API
|
||||
- [x] Add bluetooth pairing code output to rnodeconf
|
||||
- The current `0.6.x` release cycle aims at completing
|
||||
- [ ] Overhauling and updating the documentation
|
||||
- [ ] Performance and memory optimisations of the Python reference implementation
|
||||
- [ ] Fixing potential bugs
|
||||
- [ ] Distributed Destination Naming System
|
||||
- [ ] Create a standalone RNS Daemon app for Android
|
||||
- [ ] Network-wide path balancing
|
||||
- [ ] Add automatic retries to all use cases of the `Request` API
|
||||
- [ ] Performance and memory optimisations of the Python reference implementation
|
||||
- [ ] Fixing bugs discovered while operating Reticulum systems and applications
|
||||
|
||||
## Primary Efforts
|
||||
The development path for Reticulum is currently laid out in five distinct areas: *Comprehensibility*, *Universality*, *Functionality*, *Usability & Utility* and *Interfaceability*. Conceptualising the development of Reticulum into these areas serves to advance the implementation and work towards the Foundational Goals & Values of Reticulum.
|
||||
@@ -40,12 +40,12 @@ These efforts are aimed at improving the ease of which Reticulum is understood,
|
||||
- Update NomadNet screenshots
|
||||
- Update Sideband screenshots
|
||||
- Installation
|
||||
- Add a *Reticulum On Raspberry Pi* section
|
||||
- Update *Reticulum On Android* section if necessary
|
||||
- Update Android install documentation.
|
||||
- [x] Add a *Reticulum On Raspberry Pi* section
|
||||
- [x] Update *Reticulum On Android* section if necessary
|
||||
- [x] Update Android install documentation.
|
||||
- Communications hardware section
|
||||
- Add information about RNode external displays.
|
||||
- Packet radio modems.
|
||||
- [x] Packet radio modems.
|
||||
- Possibly add other relevant types here as well.
|
||||
- Setup *Best Practices For...* / *Installation Examples* section.
|
||||
- Home or office (example)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 68929b7207a84262cbd1b68cc50e2489
|
||||
config: 0f2f9f8af8c66687a92c6541875995ff
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
.. _interfaces-main:
|
||||
|
||||
********************
|
||||
Supported Interfaces
|
||||
********************
|
||||
**********************
|
||||
Configuring Interfaces
|
||||
**********************
|
||||
|
||||
Reticulum supports using many kinds of devices as networking interfaces, and
|
||||
allows you to mix and match them in any way you choose. The number of distinct
|
||||
@@ -761,3 +761,74 @@ conserve bandwidth, while very fast networks can support applications that
|
||||
need very frequent announces. Reticulum implements these mechanisms to ensure
|
||||
that a large span of network types can seamlessly *co-exist* and interconnect.
|
||||
|
||||
.. _interfaces-ingress-control:
|
||||
|
||||
New Destination Rate Limiting
|
||||
=============================
|
||||
|
||||
On public interfaces, where anyone may connect and announce new destinations,
|
||||
it can be useful to control the rate at which announces for *new* destinations are
|
||||
processed.
|
||||
|
||||
If a large influx of announces for newly created or previously unknown destinations
|
||||
occur within a short amount of time, Reticulum will place these announces on hold,
|
||||
so that announce traffic for known and previously established destinations can
|
||||
continue to be processed without interruptions.
|
||||
|
||||
After the burst subsides, and an additional waiting period has passed, the held
|
||||
announces will be released at a slow rate, until the hold queue is cleared. This
|
||||
also means, that should a node decide to connect to a public interface, announce
|
||||
a large amount of bogus destinations, and then disconnect, these destination will
|
||||
never make it into path tables and waste network bandwidth on retransmitted
|
||||
announces.
|
||||
|
||||
**It's important to note** that the ingress control works at the level of *individual
|
||||
sub-interfaces*. As an example, this means that one client on a :ref:`TCP Server Interface<interfaces-tcps>`
|
||||
cannot disrupt processing of incoming announces for other connected clients on the same
|
||||
:ref:`TCP Server Interface<interfaces-tcps>`. All other clients on the same interface will still have new announces
|
||||
processed without interruption.
|
||||
|
||||
By default, Reticulum will handle this automatically, and ingress announce
|
||||
control will be enabled on interface where it is sensible to do so. It should
|
||||
generally not be neccessary to modify the ingress control configuration,
|
||||
but all the parameters are exposed for configuration if needed.
|
||||
|
||||
* | The ``ingress_control`` option tells Reticulum whether or not
|
||||
to enable announce ingress control on the interface. Defaults to
|
||||
``True``.
|
||||
|
||||
* | The ``ic_new_time`` option configures how long (in seconds) an
|
||||
interface is considered newly spawned. Defaults to ``2*60*60`` seconds. This
|
||||
option is useful on publicly accessible interfaces that spawn new
|
||||
sub-interfaces when a new client connects.
|
||||
|
||||
* | The ``ic_burst_freq_new`` option sets the maximum announce ingress
|
||||
frequency for newly spawned interfaces. Defaults to ``3.5``
|
||||
announces per second.
|
||||
|
||||
* | The ``ic_burst_freq`` option sets the maximum announce ingress
|
||||
frequency for other interfaces. Defaults to ``12`` announces
|
||||
per second.
|
||||
|
||||
*If an interface exceeds its burst frequency, incoming announces
|
||||
for unknown destinations will be temporarily held in a queue, and
|
||||
not processed until later.*
|
||||
|
||||
* | The ``ic_max_held_announces`` option sets the maximum amount of
|
||||
unique announces that will be held in the queue. Any additional
|
||||
unique announces will be dropped. Defaults to ``256`` announces.
|
||||
|
||||
* | The ``ic_burst_hold`` option sets how much time (in seconds) must
|
||||
pass after the burst frequency drops below its threshold, for the
|
||||
announce burst to be considered cleared. Defaults to ``60``
|
||||
seconds.
|
||||
|
||||
* | The ``ic_burst_penalty`` option sets how much time (in seconds) must
|
||||
pass after the burst is considered cleared, before held announces can
|
||||
start being released from the queue. Defaults to ``5*60``
|
||||
seconds.
|
||||
|
||||
* | The ``ic_held_release_interval`` option sets how much time (in seconds)
|
||||
must pass between releasing each held announce from the queue. Defaults
|
||||
to ``30`` seconds.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ guide the design of Reticulum:
|
||||
it can be easily modified and replicated by anyone interested in doing so.
|
||||
* **Very low bandwidth requirements**
|
||||
Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||
as *500 bits per second*.
|
||||
as *5 bits per second*.
|
||||
* **Encryption by default**
|
||||
Reticulum must use strong encryption by default for all communication.
|
||||
* **Initiator Anonymity**
|
||||
@@ -595,7 +595,7 @@ or less any medium that allows you to send and receive data, which satisfies som
|
||||
minimum requirements.
|
||||
|
||||
The communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
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
|
||||
runtime environment.
|
||||
|
||||
|
||||
@@ -99,6 +99,17 @@ configuration file is created. The default configuration looks like this:
|
||||
instance_control_port = 37429
|
||||
|
||||
|
||||
# On systems where running instances may not have access
|
||||
# to the same shared Reticulum configuration directory,
|
||||
# it is still possible to allow full interactivity for
|
||||
# running instances, by manually specifying a shared RPC
|
||||
# key. In almost all cases, this option is not needed, but
|
||||
# it can be useful on operating systems such as Android.
|
||||
# The key must be specified as bytes in hexadecimal.
|
||||
|
||||
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
|
||||
|
||||
|
||||
# You can configure Reticulum to panic and forcibly close
|
||||
# if an unrecoverable interface error occurs, such as the
|
||||
# hardware device for an interface disappearing. This is
|
||||
@@ -301,19 +312,23 @@ Filter output to only show some interfaces:
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-j] [-v] [filter]
|
||||
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
|
||||
[-r] [-j] [-v] [filter]
|
||||
|
||||
Reticulum Network Stack Status
|
||||
|
||||
positional arguments:
|
||||
filter only display interfaces with names including filter
|
||||
filter only display interfaces with names including filter
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-j, --json output in JSON format
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-A, --announce-stats show announce stats
|
||||
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
|
||||
-r, --reverse reverse sorting
|
||||
-j, --json output in JSON format
|
||||
-v, --verbose
|
||||
|
||||
|
||||
@@ -438,7 +453,7 @@ Resolve path to a destination:
|
||||
.. code:: text
|
||||
|
||||
usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
|
||||
[-w seconds] [-v] [destination]
|
||||
[-x] [-w seconds] [-v] [destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
@@ -453,6 +468,7 @@ Resolve path to a destination:
|
||||
-r, --rates show announce rate info
|
||||
-d, --drop remove the path to a destination
|
||||
-D, --drop-announces drop all queued announces
|
||||
-x, --drop-via drop all paths via specified transport instance
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
|
||||
@@ -764,6 +780,9 @@ Reticulum as a System Service
|
||||
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
|
||||
service and have it start automatically at boot.
|
||||
|
||||
Systemwide Service
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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 able to execute it. In this case, you can simply symlink the ``rnsd`` program
|
||||
@@ -811,3 +830,48 @@ If you want to automatically start ``rnsd`` at boot, run:
|
||||
.. code:: text
|
||||
|
||||
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?
|
||||
============================
|
||||
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,
|
||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||
of the types of interfaces Reticulum was designed for.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.6.0 beta',
|
||||
VERSION: '0.7.2 beta',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
+11
-12
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Code Examples - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Code Examples - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -258,7 +258,7 @@ program.</p>
|
||||
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
|
||||
<span class="c1"># and communicated with. Destinations can also announce their</span>
|
||||
<span class="c1"># existence, which will let the network know they are reachable</span>
|
||||
<span class="c1"># and autoomatically create paths to them, from anywhere else</span>
|
||||
<span class="c1"># and automatically create paths to them, from anywhere else</span>
|
||||
<span class="c1"># in the network.</span>
|
||||
<span class="n">destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
|
||||
<span class="n">identity</span><span class="p">,</span>
|
||||
@@ -269,7 +269,7 @@ program.</p>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># We configure the destination to automatically prove all</span>
|
||||
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
|
||||
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
|
||||
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
||||
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
|
||||
<span class="c1"># tries to communicate with the destination know whether their</span>
|
||||
@@ -375,7 +375,7 @@ notifications about announces from relevant destinations.</p>
|
||||
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
|
||||
<span class="c1"># and communicated with. Destinations can also announce their</span>
|
||||
<span class="c1"># existence, which will let the network know they are reachable</span>
|
||||
<span class="c1"># and autoomatically create paths to them, from anywhere else</span>
|
||||
<span class="c1"># and automatically create paths to them, from anywhere else</span>
|
||||
<span class="c1"># in the network.</span>
|
||||
<span class="n">destination_1</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
|
||||
<span class="n">identity</span><span class="p">,</span>
|
||||
@@ -396,7 +396,7 @@ notifications about announces from relevant destinations.</p>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># We configure the destinations to automatically prove all</span>
|
||||
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
|
||||
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
|
||||
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
||||
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
|
||||
<span class="c1"># tries to communicate with the destination know whether their</span>
|
||||
@@ -697,7 +697,7 @@ the Packet interface.</p>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># We configure the destination to automatically prove all</span>
|
||||
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
|
||||
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
|
||||
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
|
||||
<span class="c1"># back to the sender of that packet.</span>
|
||||
<span class="n">echo_destination</span><span class="o">.</span><span class="n">set_proof_strategy</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">PROVE_ALL</span><span class="p">)</span>
|
||||
@@ -3084,8 +3084,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"># a potential timeout in receiving the</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">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">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">start</span><span class="p">()</span>
|
||||
|
||||
<span class="c1"># This job just sleeps for the specified</span>
|
||||
@@ -3322,7 +3321,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<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"/>
|
||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -139,7 +139,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -165,7 +165,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -179,7 +179,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -373,6 +373,8 @@
|
||||
<li><a href="reference.html#RNS.Link.get_establishment_rate">get_establishment_rate() (RNS.Link method)</a>
|
||||
</li>
|
||||
<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><a href="reference.html#RNS.Resource.get_parts">get_parts() (RNS.Resource method)</a>
|
||||
</li>
|
||||
@@ -389,6 +391,8 @@
|
||||
</li>
|
||||
</ul></li>
|
||||
<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>
|
||||
</ul></td>
|
||||
<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>
|
||||
<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><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
|
||||
</li>
|
||||
<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><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
|
||||
|
||||
@@ -502,6 +510,8 @@
|
||||
</li>
|
||||
</ul></td>
|
||||
<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>
|
||||
<li><a href="reference.html#RNS.Reticulum.MTU">MTU (RNS.Reticulum attribute)</a>
|
||||
@@ -520,6 +530,8 @@
|
||||
</li>
|
||||
</ul></td>
|
||||
<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>
|
||||
<li><a href="reference.html#RNS.Link.no_outbound_for">no_outbound_for() (RNS.Link method)</a>
|
||||
@@ -656,10 +668,12 @@
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Identity.to_file">to_file() (RNS.Identity method)</a>
|
||||
</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>
|
||||
</ul></td>
|
||||
<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>
|
||||
<li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a>
|
||||
@@ -723,7 +737,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Getting Started Fast - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Getting Started Fast - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -758,7 +758,7 @@ section of this manual.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head><meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Supported Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Communications Hardware - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Communications Hardware - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -441,7 +441,7 @@ can be used with Reticulum. This includes virtual software modems such as
|
||||
<div class="context">
|
||||
<span>Next</span>
|
||||
</div>
|
||||
<div class="title">Supported Interfaces</div>
|
||||
<div class="title">Configuring Interfaces</div>
|
||||
</div>
|
||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
||||
</a>
|
||||
@@ -519,7 +519,7 @@ can be used with Reticulum. This includes virtual software modems such as
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -330,7 +330,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="toctree-l2"><a class="reference internal" href="hardware.html#packet-radio-modems">Packet Radio Modems</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a><ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#auto-interface">Auto Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#i2p-interface">I2P Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#tcp-server-interface">TCP Server Interface</a></li>
|
||||
@@ -344,6 +344,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#common-interface-options">Common Interface Options</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#interface-modes">Interface Modes</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#announce-rate-control">Announce Rate Control</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#new-destination-rate-limiting">New Destination Rate Limiting</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a><ul>
|
||||
@@ -467,7 +468,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Supported Interfaces - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Configuring Interfaces - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -221,8 +221,8 @@
|
||||
</label>
|
||||
</div>
|
||||
<article role="main">
|
||||
<section id="supported-interfaces">
|
||||
<span id="interfaces-main"></span><h1>Supported Interfaces<a class="headerlink" href="#supported-interfaces" title="Permalink to this heading">#</a></h1>
|
||||
<section id="configuring-interfaces">
|
||||
<span id="interfaces-main"></span><h1>Configuring Interfaces<a class="headerlink" href="#configuring-interfaces" title="Permalink to this heading">#</a></h1>
|
||||
<p>Reticulum supports using many kinds of devices as networking interfaces, and
|
||||
allows you to mix and match them in any way you choose. The number of distinct
|
||||
network topologies you can create with Reticulum is more or less endless, but
|
||||
@@ -937,6 +937,91 @@ conserve bandwidth, while very fast networks can support applications that
|
||||
need very frequent announces. Reticulum implements these mechanisms to ensure
|
||||
that a large span of network types can seamlessly <em>co-exist</em> and interconnect.</p>
|
||||
</section>
|
||||
<section id="new-destination-rate-limiting">
|
||||
<span id="interfaces-ingress-control"></span><h2>New Destination Rate Limiting<a class="headerlink" href="#new-destination-rate-limiting" title="Permalink to this heading">#</a></h2>
|
||||
<p>On public interfaces, where anyone may connect and announce new destinations,
|
||||
it can be useful to control the rate at which announces for <em>new</em> destinations are
|
||||
processed.</p>
|
||||
<p>If a large influx of announces for newly created or previously unknown destinations
|
||||
occur within a short amount of time, Reticulum will place these announces on hold,
|
||||
so that announce traffic for known and previously established destinations can
|
||||
continue to be processed without interruptions.</p>
|
||||
<p>After the burst subsides, and an additional waiting period has passed, the held
|
||||
announces will be released at a slow rate, until the hold queue is cleared. This
|
||||
also means, that should a node decide to connect to a public interface, announce
|
||||
a large amount of bogus destinations, and then disconnect, these destination will
|
||||
never make it into path tables and waste network bandwidth on retransmitted
|
||||
announces.</p>
|
||||
<p><strong>It’s important to note</strong> that the ingress control works at the level of <em>individual
|
||||
sub-interfaces</em>. As an example, this means that one client on a <a class="reference internal" href="#interfaces-tcps"><span class="std std-ref">TCP Server Interface</span></a>
|
||||
cannot disrupt processing of incoming announces for other connected clients on the same
|
||||
<a class="reference internal" href="#interfaces-tcps"><span class="std std-ref">TCP Server Interface</span></a>. All other clients on the same interface will still have new announces
|
||||
processed without interruption.</p>
|
||||
<p>By default, Reticulum will handle this automatically, and ingress announce
|
||||
control will be enabled on interface where it is sensible to do so. It should
|
||||
generally not be neccessary to modify the ingress control configuration,
|
||||
but all the parameters are exposed for configuration if needed.</p>
|
||||
<blockquote>
|
||||
<div><ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ingress_control</span></code> option tells Reticulum whether or not
|
||||
to enable announce ingress control on the interface. Defaults to
|
||||
<code class="docutils literal notranslate"><span class="pre">True</span></code>.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_new_time</span></code> option configures how long (in seconds) an
|
||||
interface is considered newly spawned. Defaults to <code class="docutils literal notranslate"><span class="pre">2*60*60</span></code> seconds. This
|
||||
option is useful on publicly accessible interfaces that spawn new
|
||||
sub-interfaces when a new client connects.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_freq_new</span></code> option sets the maximum announce ingress
|
||||
frequency for newly spawned interfaces. Defaults to <code class="docutils literal notranslate"><span class="pre">3.5</span></code>
|
||||
announces per second.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_freq</span></code> option sets the maximum announce ingress
|
||||
frequency for other interfaces. Defaults to <code class="docutils literal notranslate"><span class="pre">12</span></code> announces
|
||||
per second.</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p><em>If an interface exceeds its burst frequency, incoming announces
|
||||
for unknown destinations will be temporarily held in a queue, and
|
||||
not processed until later.</em></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_max_held_announces</span></code> option sets the maximum amount of
|
||||
unique announces that will be held in the queue. Any additional
|
||||
unique announces will be dropped. Defaults to <code class="docutils literal notranslate"><span class="pre">256</span></code> announces.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_hold</span></code> option sets how much time (in seconds) must
|
||||
pass after the burst frequency drops below its threshold, for the
|
||||
announce burst to be considered cleared. Defaults to <code class="docutils literal notranslate"><span class="pre">60</span></code>
|
||||
seconds.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_penalty</span></code> option sets how much time (in seconds) must
|
||||
pass after the burst is considered cleared, before held announces can
|
||||
start being released from the queue. Defaults to <code class="docutils literal notranslate"><span class="pre">5*60</span></code>
|
||||
seconds.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_held_release_interval</span></code> option sets how much time (in seconds)
|
||||
must pass between releasing each held announce from the queue. Defaults
|
||||
to <code class="docutils literal notranslate"><span class="pre">30</span></code> seconds.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
@@ -995,7 +1080,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
|
||||
<div class="toc-tree-container">
|
||||
<div class="toc-tree">
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Supported Interfaces</a><ul>
|
||||
<li><a class="reference internal" href="#">Configuring Interfaces</a><ul>
|
||||
<li><a class="reference internal" href="#auto-interface">Auto Interface</a></li>
|
||||
<li><a class="reference internal" href="#i2p-interface">I2P Interface</a></li>
|
||||
<li><a class="reference internal" href="#tcp-server-interface">TCP Server Interface</a></li>
|
||||
@@ -1009,6 +1094,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
|
||||
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
|
||||
<li><a class="reference internal" href="#interface-modes">Interface Modes</a></li>
|
||||
<li><a class="reference internal" href="#announce-rate-control">Announce Rate Control</a></li>
|
||||
<li><a class="reference internal" href="#new-destination-rate-limiting">New Destination Rate Limiting</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1020,7 +1106,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head><meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Supported Interfaces" href="interfaces.html" />
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Building Networks - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Building Networks - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -413,7 +413,7 @@ connected outliers are now an integral part of the network.</p>
|
||||
<span>Previous</span>
|
||||
</div>
|
||||
|
||||
<div class="title">Supported Interfaces</div>
|
||||
<div class="title">Configuring Interfaces</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
@@ -467,7 +467,7 @@ connected outliers are now an integral part of the network.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
Binary file not shown.
+87
-11
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>API Reference - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>API Reference - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -253,7 +253,7 @@ other programs to use on demand.</p>
|
||||
<dt class="sig sig-object py" id="RNS.Reticulum.MTU">
|
||||
<span class="sig-name descname"><span class="pre">MTU</span></span><em class="property"><span class="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
|
||||
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
|
||||
completely break compatibility with all other RNS networks. An identical
|
||||
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,
|
||||
it will eventually be dropped.</p>
|
||||
<p>This value will be applied by default to all created interfaces,
|
||||
but it can be configured individually on a per-interface basis.</p>
|
||||
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>
|
||||
|
||||
<dl class="py method">
|
||||
@@ -850,7 +865,7 @@ unless other app_data is specified in the <em>announce</em> method.</p>
|
||||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Packet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">create_receipt</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Packet" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>The Packet class is used to create packet instances that can be sent
|
||||
over a Reticulum network. Packets will automatically be encrypted if
|
||||
they are adressed to a <code class="docutils literal notranslate"><span class="pre">RNS.Destination.SINGLE</span></code> destination,
|
||||
they are addressed to a <code class="docutils literal notranslate"><span class="pre">RNS.Destination.SINGLE</span></code> destination,
|
||||
<code class="docutils literal notranslate"><span class="pre">RNS.Destination.GROUP</span></code> destination or a <a class="reference internal" href="#api-link"><span class="std std-ref">RNS.Link</span></a>.</p>
|
||||
<p>For <code class="docutils literal notranslate"><span class="pre">RNS.Destination.GROUP</span></code> destinations, Reticulum will use the
|
||||
pre-shared key configured for the destination. All packets to group
|
||||
@@ -1057,6 +1072,50 @@ thus preserved. This method can be used for authentication.</p>
|
||||
</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">
|
||||
<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>
|
||||
@@ -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>
|
||||
<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 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>
|
||||
</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>
|
||||
<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 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>
|
||||
</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>
|
||||
<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 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>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -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.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.MINIMUM_BITRATE"><code class="docutils literal notranslate"><span class="pre">MINIMUM_BITRATE</span></code></a></li>
|
||||
<li><a class="reference internal" href="#RNS.Reticulum.get_instance"><code class="docutils literal notranslate"><span class="pre">get_instance()</span></code></a></li>
|
||||
<li><a class="reference internal" href="#RNS.Reticulum.should_use_implicit_proof"><code class="docutils literal notranslate"><span class="pre">should_use_implicit_proof()</span></code></a></li>
|
||||
<li><a class="reference internal" href="#RNS.Reticulum.transport_enabled"><code class="docutils literal notranslate"><span class="pre">transport_enabled()</span></code></a></li>
|
||||
</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.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.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.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_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.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>
|
||||
@@ -2013,7 +2089,7 @@ will announce it.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.6.0 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.7.2 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||
@@ -138,7 +138,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -164,7 +164,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -178,7 +178,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -262,7 +262,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Support Reticulum - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Support Reticulum - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Support Reticulum</a></li>
|
||||
@@ -330,7 +330,7 @@ report issues, suggest functionality and contribute code to Reticulum.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -286,7 +286,7 @@ it can be easily modified and replicated by anyone interested in doing so.</p>
|
||||
</li>
|
||||
<li><dl class="simple">
|
||||
<dt><strong>Very low bandwidth requirements</strong></dt><dd><p>Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||
as <em>500 bits per second</em>.</p>
|
||||
as <em>5 bits per second</em>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</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
|
||||
minimum requirements.</p>
|
||||
<p>The communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
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
|
||||
runtime environment.</p>
|
||||
<p>That being said, this reference setup has been outlined to provide a common platform for anyone
|
||||
@@ -1196,7 +1196,7 @@ those risks are acceptable to you.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
+73
-14
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -307,6 +307,17 @@ configuration file is created. The default configuration looks like this:</p>
|
||||
<span class="n">instance_control_port</span> <span class="o">=</span> <span class="mi">37429</span>
|
||||
|
||||
|
||||
<span class="c1"># On systems where running instances may not have access</span>
|
||||
<span class="c1"># to the same shared Reticulum configuration directory,</span>
|
||||
<span class="c1"># it is still possible to allow full interactivity for</span>
|
||||
<span class="c1"># running instances, by manually specifying a shared RPC</span>
|
||||
<span class="c1"># key. In almost all cases, this option is not needed, but</span>
|
||||
<span class="c1"># it can be useful on operating systems such as Android.</span>
|
||||
<span class="c1"># The key must be specified as bytes in hexadecimal.</span>
|
||||
|
||||
<span class="c1"># rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790</span>
|
||||
|
||||
|
||||
<span class="c1"># You can configure Reticulum to panic and forcibly close</span>
|
||||
<span class="c1"># if an unrecoverable interface error occurs, such as the</span>
|
||||
<span class="c1"># hardware device for an interface disappearing. This is</span>
|
||||
@@ -481,19 +492,23 @@ Reticulum Transport Instance <5245a8efe1788c6a1cd36144a270e13b> running
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><strong>All Command-Line Options</strong></p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-j] [-v] [filter]
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
|
||||
[-r] [-j] [-v] [filter]
|
||||
|
||||
Reticulum Network Stack Status
|
||||
|
||||
positional arguments:
|
||||
filter only display interfaces with names including filter
|
||||
filter only display interfaces with names including filter
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-j, --json output in JSON format
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-A, --announce-stats show announce stats
|
||||
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
|
||||
-r, --reverse reverse sorting
|
||||
-j, --json output in JSON format
|
||||
-v, --verbose
|
||||
</pre></div>
|
||||
</div>
|
||||
@@ -595,7 +610,7 @@ Path found, destination <c89b4da064bf66d280f0e4d8abfd9806> is 4 hops away
|
||||
</div>
|
||||
<p><strong>All Command-Line Options</strong></p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
|
||||
[-w seconds] [-v] [destination]
|
||||
[-x] [-w seconds] [-v] [destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
@@ -610,6 +625,7 @@ options:
|
||||
-r, --rates show announce rate info
|
||||
-d, --drop remove the path to a destination
|
||||
-D, --drop-announces drop all queued announces
|
||||
-x, --drop-via drop all paths via specified transport instance
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
</pre></div>
|
||||
@@ -870,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>
|
||||
<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>
|
||||
<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
|
||||
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
|
||||
@@ -910,6 +928,43 @@ WantedBy=multi-user.target
|
||||
</pre></div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@@ -984,7 +1039,11 @@ WantedBy=multi-user.target
|
||||
</li>
|
||||
<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="#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>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -998,7 +1057,7 @@ WantedBy=multi-user.target
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
|
||||
<title>What is Reticulum? - Reticulum Network Stack 0.6.0 beta documentation</title>
|
||||
<title>What is Reticulum? - Reticulum Network Stack 0.7.2 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
@@ -141,7 +141,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.6.0 beta documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.2 beta documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -167,7 +167,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.6.0 beta documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.2 beta documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -181,7 +181,7 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
|
||||
@@ -293,7 +293,7 @@ considered complete and stable at the moment, but could change if absolutely war
|
||||
<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>
|
||||
<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,
|
||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||
of the types of interfaces Reticulum was designed for.</p>
|
||||
@@ -434,7 +434,7 @@ want to help out with this, or can help sponsor an audit, please do get in touch
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=c96a9f3e"></script>
|
||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=50521ac4"></script>
|
||||
<script src="_static/doctools.js?v=888ff710"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
|
||||
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
.. _interfaces-main:
|
||||
|
||||
********************
|
||||
Supported Interfaces
|
||||
********************
|
||||
**********************
|
||||
Configuring Interfaces
|
||||
**********************
|
||||
|
||||
Reticulum supports using many kinds of devices as networking interfaces, and
|
||||
allows you to mix and match them in any way you choose. The number of distinct
|
||||
@@ -761,3 +761,74 @@ conserve bandwidth, while very fast networks can support applications that
|
||||
need very frequent announces. Reticulum implements these mechanisms to ensure
|
||||
that a large span of network types can seamlessly *co-exist* and interconnect.
|
||||
|
||||
.. _interfaces-ingress-control:
|
||||
|
||||
New Destination Rate Limiting
|
||||
=============================
|
||||
|
||||
On public interfaces, where anyone may connect and announce new destinations,
|
||||
it can be useful to control the rate at which announces for *new* destinations are
|
||||
processed.
|
||||
|
||||
If a large influx of announces for newly created or previously unknown destinations
|
||||
occur within a short amount of time, Reticulum will place these announces on hold,
|
||||
so that announce traffic for known and previously established destinations can
|
||||
continue to be processed without interruptions.
|
||||
|
||||
After the burst subsides, and an additional waiting period has passed, the held
|
||||
announces will be released at a slow rate, until the hold queue is cleared. This
|
||||
also means, that should a node decide to connect to a public interface, announce
|
||||
a large amount of bogus destinations, and then disconnect, these destination will
|
||||
never make it into path tables and waste network bandwidth on retransmitted
|
||||
announces.
|
||||
|
||||
**It's important to note** that the ingress control works at the level of *individual
|
||||
sub-interfaces*. As an example, this means that one client on a :ref:`TCP Server Interface<interfaces-tcps>`
|
||||
cannot disrupt processing of incoming announces for other connected clients on the same
|
||||
:ref:`TCP Server Interface<interfaces-tcps>`. All other clients on the same interface will still have new announces
|
||||
processed without interruption.
|
||||
|
||||
By default, Reticulum will handle this automatically, and ingress announce
|
||||
control will be enabled on interface where it is sensible to do so. It should
|
||||
generally not be neccessary to modify the ingress control configuration,
|
||||
but all the parameters are exposed for configuration if needed.
|
||||
|
||||
* | The ``ingress_control`` option tells Reticulum whether or not
|
||||
to enable announce ingress control on the interface. Defaults to
|
||||
``True``.
|
||||
|
||||
* | The ``ic_new_time`` option configures how long (in seconds) an
|
||||
interface is considered newly spawned. Defaults to ``2*60*60`` seconds. This
|
||||
option is useful on publicly accessible interfaces that spawn new
|
||||
sub-interfaces when a new client connects.
|
||||
|
||||
* | The ``ic_burst_freq_new`` option sets the maximum announce ingress
|
||||
frequency for newly spawned interfaces. Defaults to ``3.5``
|
||||
announces per second.
|
||||
|
||||
* | The ``ic_burst_freq`` option sets the maximum announce ingress
|
||||
frequency for other interfaces. Defaults to ``12`` announces
|
||||
per second.
|
||||
|
||||
*If an interface exceeds its burst frequency, incoming announces
|
||||
for unknown destinations will be temporarily held in a queue, and
|
||||
not processed until later.*
|
||||
|
||||
* | The ``ic_max_held_announces`` option sets the maximum amount of
|
||||
unique announces that will be held in the queue. Any additional
|
||||
unique announces will be dropped. Defaults to ``256`` announces.
|
||||
|
||||
* | The ``ic_burst_hold`` option sets how much time (in seconds) must
|
||||
pass after the burst frequency drops below its threshold, for the
|
||||
announce burst to be considered cleared. Defaults to ``60``
|
||||
seconds.
|
||||
|
||||
* | The ``ic_burst_penalty`` option sets how much time (in seconds) must
|
||||
pass after the burst is considered cleared, before held announces can
|
||||
start being released from the queue. Defaults to ``5*60``
|
||||
seconds.
|
||||
|
||||
* | The ``ic_held_release_interval`` option sets how much time (in seconds)
|
||||
must pass between releasing each held announce from the queue. Defaults
|
||||
to ``30`` seconds.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ guide the design of Reticulum:
|
||||
it can be easily modified and replicated by anyone interested in doing so.
|
||||
* **Very low bandwidth requirements**
|
||||
Reticulum should be able to function reliably over links with a transmission capacity as low
|
||||
as *500 bits per second*.
|
||||
as *5 bits per second*.
|
||||
* **Encryption by default**
|
||||
Reticulum must use strong encryption by default for all communication.
|
||||
* **Initiator Anonymity**
|
||||
@@ -595,7 +595,7 @@ or less any medium that allows you to send and receive data, which satisfies som
|
||||
minimum requirements.
|
||||
|
||||
The communication channel must support at least half-duplex operation, and provide an average
|
||||
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
|
||||
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
|
||||
runtime environment.
|
||||
|
||||
|
||||
+72
-8
@@ -99,6 +99,17 @@ configuration file is created. The default configuration looks like this:
|
||||
instance_control_port = 37429
|
||||
|
||||
|
||||
# On systems where running instances may not have access
|
||||
# to the same shared Reticulum configuration directory,
|
||||
# it is still possible to allow full interactivity for
|
||||
# running instances, by manually specifying a shared RPC
|
||||
# key. In almost all cases, this option is not needed, but
|
||||
# it can be useful on operating systems such as Android.
|
||||
# The key must be specified as bytes in hexadecimal.
|
||||
|
||||
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
|
||||
|
||||
|
||||
# You can configure Reticulum to panic and forcibly close
|
||||
# if an unrecoverable interface error occurs, such as the
|
||||
# hardware device for an interface disappearing. This is
|
||||
@@ -301,19 +312,23 @@ Filter output to only show some interfaces:
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-j] [-v] [filter]
|
||||
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
|
||||
[-r] [-j] [-v] [filter]
|
||||
|
||||
Reticulum Network Stack Status
|
||||
|
||||
positional arguments:
|
||||
filter only display interfaces with names including filter
|
||||
filter only display interfaces with names including filter
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-j, --json output in JSON format
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-a, --all show all interfaces
|
||||
-A, --announce-stats show announce stats
|
||||
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
|
||||
-r, --reverse reverse sorting
|
||||
-j, --json output in JSON format
|
||||
-v, --verbose
|
||||
|
||||
|
||||
@@ -438,7 +453,7 @@ Resolve path to a destination:
|
||||
.. code:: text
|
||||
|
||||
usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
|
||||
[-w seconds] [-v] [destination]
|
||||
[-x] [-w seconds] [-v] [destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
@@ -453,6 +468,7 @@ Resolve path to a destination:
|
||||
-r, --rates show announce rate info
|
||||
-d, --drop remove the path to a destination
|
||||
-D, --drop-announces drop all queued announces
|
||||
-x, --drop-via drop all paths via specified transport instance
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
|
||||
@@ -764,6 +780,9 @@ Reticulum as a System Service
|
||||
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
|
||||
service and have it start automatically at boot.
|
||||
|
||||
Systemwide Service
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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 able to execute it. In this case, you can simply symlink the ``rnsd`` program
|
||||
@@ -811,3 +830,48 @@ If you want to automatically start ``rnsd`` at boot, run:
|
||||
.. code:: text
|
||||
|
||||
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?
|
||||
============================
|
||||
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,
|
||||
ad-hoc WiFi, free-space optical links and similar systems are all examples
|
||||
of the types of interfaces Reticulum was designed for.
|
||||
|
||||
@@ -49,6 +49,7 @@ setuptools.setup(
|
||||
'rnid=RNS.Utilities.rnid:main',
|
||||
'rncp=RNS.Utilities.rncp:main',
|
||||
'rnx=RNS.Utilities.rnx:main',
|
||||
'rnir=RNS.Utilities.rnir:main',
|
||||
'rnodeconf=RNS.Utilities.rnodeconf:main',
|
||||
]
|
||||
},
|
||||
|
||||
+29
-4
@@ -140,6 +140,7 @@ class TestLink(unittest.TestCase):
|
||||
pstart = time.time()
|
||||
print("Sending "+str(num_packets)+" link packets of "+str(packet_size)+" bytes...")
|
||||
for i in range(0, num_packets):
|
||||
time.sleep(0.003)
|
||||
b += packet_size
|
||||
data = os.urandom(packet_size)
|
||||
start = time.time()
|
||||
@@ -188,6 +189,9 @@ class TestLink(unittest.TestCase):
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
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")
|
||||
|
||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||
@@ -203,6 +207,7 @@ class TestLink(unittest.TestCase):
|
||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||
start = time.time()
|
||||
|
||||
# This is a hack, don't do it. Use the callbacks instead.
|
||||
while resource.status < RNS.Resource.COMPLETE:
|
||||
time.sleep(0.01)
|
||||
|
||||
@@ -224,6 +229,9 @@ class TestLink(unittest.TestCase):
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
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")
|
||||
|
||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||
@@ -239,6 +247,7 @@ class TestLink(unittest.TestCase):
|
||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||
start = time.time()
|
||||
|
||||
# This is a hack, don't do it. Use the callbacks instead.
|
||||
while resource.status < RNS.Resource.COMPLETE:
|
||||
time.sleep(0.01)
|
||||
|
||||
@@ -260,6 +269,9 @@ class TestLink(unittest.TestCase):
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
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")
|
||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||
|
||||
@@ -274,6 +286,7 @@ class TestLink(unittest.TestCase):
|
||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||
start = time.time()
|
||||
|
||||
# This is a hack, don't do it. Use the callbacks instead.
|
||||
while resource.status < RNS.Resource.COMPLETE:
|
||||
time.sleep(0.01)
|
||||
|
||||
@@ -300,6 +313,9 @@ class TestLink(unittest.TestCase):
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
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")
|
||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||
|
||||
@@ -314,6 +330,7 @@ class TestLink(unittest.TestCase):
|
||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||
start = time.time()
|
||||
|
||||
# This is a hack, don't do it. Use the callbacks instead.
|
||||
while resource.status < RNS.Resource.COMPLETE:
|
||||
time.sleep(0.01)
|
||||
|
||||
@@ -325,6 +342,10 @@ class TestLink(unittest.TestCase):
|
||||
time.sleep(0.5)
|
||||
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")
|
||||
def test_9_large_resource(self):
|
||||
if RNS.Cryptography.backend() == "internal":
|
||||
@@ -339,6 +360,9 @@ class TestLink(unittest.TestCase):
|
||||
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
|
||||
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")
|
||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||
|
||||
@@ -347,17 +371,18 @@ class TestLink(unittest.TestCase):
|
||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||
|
||||
resource_timeout = 120
|
||||
resource_size = 35*1000*1000
|
||||
resource_size = 50*1000*1000
|
||||
data = os.urandom(resource_size)
|
||||
print("Sending "+self.size_str(resource_size)+" resource...")
|
||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
|
||||
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)
|
||||
|
||||
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")
|
||||
|
||||
l1.teardown()
|
||||
|
||||
Reference in New Issue
Block a user