mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-07-12 11:38:11 -07:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dca2a92829 | |||
| b1b3ff7143 | |||
| a0f0f31807 | |||
| 72db6e0ef2 | |||
| b70688883a | |||
| dd3ddb9d8a | |||
| 4ba1750c01 | |||
| eacff56fe2 | |||
| 8768e163ae | |||
| c0bc19d998 | |||
| c99ec922aa | |||
| 0a6f76e41b | |||
| 156ce9cd34 | |||
| 176092ebf9 | |||
| 98817e3f9d | |||
| 276b7bb92f | |||
| d1813e1ff0 | |||
| 5355a9bb20 | |||
| 48d17a8616 | |||
| 7e603f9606 | |||
| ac6f0199f5 | |||
| 71f6f721fd | |||
| 51b7dc7cd4 | |||
| 03ddb08248 | |||
| 422dc05549 | |||
| 50c0a354c9 | |||
| e98487c1cf | |||
| 50e03a24e8 | |||
| 0cc42568c7 | |||
| 41790ca707 | |||
| 20b1bfd01e | |||
| 1be5e10abe | |||
| 13a9ebed83 | |||
| aee8c3be0c | |||
| ef8ccf67ed | |||
| 91d531b053 | |||
| 0de169836e | |||
| a2ef978208 | |||
| 5b3bb050e7 | |||
| 9b9efe5fac | |||
| 482d5ac4cb |
@@ -14,3 +14,4 @@ tests/rnsconfig/logfile*
|
||||
*.data
|
||||
*.result
|
||||
.buildinfo.bak
|
||||
docs/Reticulum Manual.*
|
||||
|
||||
+46
-140
@@ -1,9 +1,12 @@
|
||||
### 2026-05-28: RNS 1.3.3
|
||||
### 2026-07-10: RNS 1.3.8
|
||||
|
||||
This release fixes a regression in persistence of known destination on Windows.
|
||||
This release fixes various inconsistencies in link and hop-count related APIs.
|
||||
|
||||
**Changes**
|
||||
- Fixed regression in known destinations persist on Windows
|
||||
- Fixed inconsistent link traffic stats calculation
|
||||
- Fixed link hop-count metric only being available on initiator side
|
||||
- Fixed potential hop-count serialization error on transport
|
||||
- Updated `WeaveInterface` to support latest Weave firmware
|
||||
|
||||
**Verified Retrieval**
|
||||
You can retrieve and verify this release over Reticulum using the built-in `rngit release` utility. To retrieve only the installation `.whl` package, and the release manifest for future updates, you can use:
|
||||
@@ -33,6 +36,46 @@ rnid -i bc7291552be7a58f361522990465165c -V rns_*.rsm *.rsg
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
### 2026-07-03: RNS 1.3.7
|
||||
|
||||
This maintenance release improves announces propagation logic, and adds additional options for configuring announce propagation and interface behavior in transport mode.
|
||||
|
||||
**Changes**
|
||||
- Added `internal` interface mode
|
||||
- Added `recursive_prs` interface option
|
||||
- Added `announces_from_internal` interface option
|
||||
- Added new options to the manual
|
||||
- Improved and cleaned up announce propagation logic
|
||||
|
||||
### 2026-07-03: RNS 1.3.6
|
||||
|
||||
This release contained a bug in the local instance transport handling, and was superseded by version `1.3.7`.
|
||||
|
||||
### 2026-06-01: RNS 1.3.5
|
||||
|
||||
This maintenance release contains an important fix for `AutoInterface` reliability when roaming between different physical networks.
|
||||
|
||||
**Changes**
|
||||
- Fixed UDP listener replacement deadlocking inbound AutoInterface traffic when fast-roaming between physical interfaces or WiFi APs
|
||||
- Fixed some paths never resolving when using other interfaces at the same time as a deadlocked AutoInterface
|
||||
|
||||
### 2026-05-29: RNS 1.3.4
|
||||
|
||||
This release fixes a regression that could cause sub-optimal path selection under conditions where the same announce was received within a very short timespan on different interfaces, as well as a few other bugs and inefficiencies.
|
||||
|
||||
**Changes**
|
||||
- Fixed regression in inbound announce de-duplication
|
||||
- Fixed missing check for shared instance type configuration conflict
|
||||
- Fixed superfluous path state configuration on new announce from new destinations
|
||||
- Improved cleanup of stale known destinations
|
||||
- Improved shared instance RPC handling
|
||||
|
||||
### 2026-05-28: RNS 1.3.3
|
||||
|
||||
This release fixes a regression in persistence of known destination on Windows.
|
||||
|
||||
**Changes**
|
||||
- Fixed regression in known destinations persist on Windows
|
||||
|
||||
### 2026-05-28: RNS 1.3.2
|
||||
|
||||
@@ -46,35 +89,6 @@ This release adds commit signing and validation support to the `rngit` system, a
|
||||
- Added option to configure blackhole update interval
|
||||
- Added configuration option to log without timestamps
|
||||
|
||||
**Verified Retrieval**
|
||||
You can retrieve and verify this release over Reticulum using the built-in `rngit release` utility. To retrieve only the installation `.whl` package, and the release manifest for future updates, you can use:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch "latest:rns-*.whl" --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
To download all artifacts, including the documentation and source archive, you can use the following command:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch latest:all --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include a signed `rsm` release manifest and `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rngit` or `rnid`. To perform an offline verification of all release artifacts using a manifest:
|
||||
|
||||
```sh
|
||||
rngit release rns_*.rsm verify --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
To verify release artifacts using individual `rsg` files, while also verifying the manifest itself, download the `rsm` and `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V rns_*.rsm *.rsg
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
|
||||
### 2026-05-22: RNS 1.3.1
|
||||
|
||||
This maintenance release fixes a single bug.
|
||||
@@ -82,34 +96,6 @@ This maintenance release fixes a single bug.
|
||||
**Changes**
|
||||
- Fixed regression in request response transfer size accumulator
|
||||
|
||||
**Verified Retrieval**
|
||||
You can retrieve and verify this release over Reticulum using the built-in `rngit release` utility. To download all artifacts, and the release manifest for future updates, you can use the following command:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch latest:all --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
To retrieve only the `.whl` package for installation, and the release manifest, you can use:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch "latest:rns-*-py3-none-any.whl" --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include a signed `rsm` release manifest and `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rngit` or `rnid`. To perform an offline verification of all release artifacts using a manifest:
|
||||
|
||||
```sh
|
||||
rngit release rns_*.rsm fetch latest:all --offline --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
To verify releases using individual `rsg` files, while also verifying the manifest itself, download the `rsm` and `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V rns_*.rsm *.rsg
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
### 2026-05-21: RNS 1.3.0
|
||||
|
||||
This maintenance release fixes a number of bugs.
|
||||
@@ -124,28 +110,6 @@ This maintenance release fixes a number of bugs.
|
||||
- Adjusted timeouts for remote operations in `rngit`
|
||||
- Updated documentation
|
||||
|
||||
**Verified Retrieval**
|
||||
You can retrieve and verify this release over Reticulum using the built-in `rngit release` utility. To download all artifacts, and the release manifest for future updates, you can use the following command:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch latest:all --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
To retrieve only the `.whl` package for installation, you can use:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch latest:rns-1.3.0-py3-none-any.whl --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include a signed `rsm` release manifest and `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsm` and `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V manifest.rsm *.rsg
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
### 2026-05-19: RNS 1.2.9
|
||||
|
||||
This release completes the operational functionality of the `rngit` system, which now has full release creation, fetch and verified update support using the `rngit release` command. Additionally, two chapters have been added to the manual should cover all the things that `rngit` is currently capable of.
|
||||
@@ -161,28 +125,6 @@ This release completes the operational functionality of the `rngit` system, whic
|
||||
- Improved page node ref link handling in `rngit`
|
||||
- Improved logging in various locations
|
||||
|
||||
**Verified Retrieval**
|
||||
You can retrieve and verify this release over Reticulum using the built-in `rngit release` utility. To download all artifacts, and the release manifest for future updates, you can use the following command:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch latest:all --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
To retrieve only the `.whl` package for installation, you can use:
|
||||
|
||||
```sh
|
||||
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/reticulum fetch latest:rns-1.2.8-py3-none-any.whl --signer bc7291552be7a58f361522990465165c
|
||||
```
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include a signed `rsm` release manifest and `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsm` and `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V manifest.rsm *.rsg
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
### 2026-05-18: RNS 1.2.8
|
||||
|
||||
This release improves the `rngit` system with signed release manifest generation and automatic artifact signing. It also includes several additions to `rnid` and various minor fixes and improvements to the `rngit` system.
|
||||
@@ -203,15 +145,6 @@ This release improves the `rngit` system with signed release manifest generation
|
||||
- Fixed various minor bugs and inconsistencies in `rngit`
|
||||
- Dropped `note` metadata field requirement from `rsg` structure
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include a signed `rsm` release manifest and `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsm` and `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V manifest.rsm *.rsg
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
### 2026-05-17: RNS 1.2.7
|
||||
|
||||
This release significantly improves the `rngit` system with fork, mirroring and empty repository creation functionality, a new work document proposals feature, improvements to the transport core reliability and efficiency and various other tweaks and improvements.
|
||||
@@ -229,15 +162,6 @@ This release significantly improves the `rngit` system with fork, mirroring and
|
||||
- Improved transfer completed feedback in `rncp`, thanks to **neutral**
|
||||
- Improved interface transport insertion and removal
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V rns*.whl
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether it is valid. If the signature cannot be verified, the file has been tampered with and should be thrown very far away in a jiffy.
|
||||
|
||||
### 2026-05-14: RNS 1.2.6
|
||||
|
||||
This release adds further improvements to the `rnid` and `rngit` utilities, and includes several bugfixes and other improvements.
|
||||
@@ -259,15 +183,6 @@ This release adds further improvements to the `rnid` and `rngit` utilities, and
|
||||
- Fixed potential race condition in interface discovery
|
||||
- Fixed `rngit` remote helper hanging on startup if no client config had been created previously, and RNS loglevel was configured at debug or higher
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V rns*.whl
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether it is valid. If the signature cannot be verified, the file has been tampered with and should be thrown very far away in a jiffy.
|
||||
|
||||
### 2026-05-09: RNS 1.2.5
|
||||
|
||||
This release brings substantial improvements to path request handling, and should significantly reduce overall network and local transport node processing loads. Path requests are now automatically ingress and egress limited per interface and sub-interface. Although the defaults are effective and sane, and should work right out of the box bring an end to practically all the PR and announce spam going on lately, the backend is fully configurable for both defaults and per interface, if you want to fiddle with the settings.
|
||||
@@ -296,15 +211,6 @@ For all node ops out there, I'd recomment updating to this at some sort of semi-
|
||||
- Ensured canonical validation functions in `rngit`
|
||||
- Lots of other small fixes and stability improvements to `rngit`
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V rns-1.2.5-py3-none-any.whl
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether it is valid. If the signature cannot be verified, the file has been tampered with and should be thrown very far away in a jiffy.
|
||||
|
||||
### 2026-05-07: RNS 1.2.4
|
||||
|
||||
This release brings a complete rewrite and update to the `rnid` utility, which is now a lot more useful, and better at finding and saving identities. It also includes a bunch of other improvements, such as expanded `rngit` functionality, better transport performance and a few bugfixes. Enjoy!
|
||||
|
||||
@@ -57,7 +57,7 @@ manual:
|
||||
make -C docs latexpdf epub
|
||||
|
||||
distcollect:
|
||||
cp docs/Reticulum\ Manual.* dist
|
||||
mv docs/Reticulum\ Manual.* dist
|
||||
|
||||
build_spkg: remove_symlinks build_sdist create_symlinks
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ userland, and can run on practically any system that runs Python 3.
|
||||
## Read The Manual
|
||||
The full documentation for Reticulum is available at [markqvist.github.io/Reticulum/manual/](https://markqvist.github.io/Reticulum/manual/).
|
||||
|
||||
You can also download the [Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf) or [as an e-book in EPUB format](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.epub).
|
||||
You can also download the [Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/releases/latest/download/Reticulum.Manual.pdf) or [as an e-book in EPUB format](https://github.com/markqvist/Reticulum/releases/latest/download/Reticulum.Manual.epub).
|
||||
|
||||
For more info, see [reticulum.network](https://reticulum.network/) and [the FAQ section of the wiki](https://github.com/markqvist/Reticulum/wiki/Frequently-Asked-Questions).
|
||||
|
||||
@@ -85,6 +85,12 @@ The Reticulum Protocol is defined entirely and authoritatively by this reference
|
||||
implementation, and its associated manual. It is maintained by Mark Qvist,
|
||||
identified by the Reticulum Identity `<bc7291552be7a58f361522990465165c>`.
|
||||
|
||||
No second-order protocol specification derivatives currently do, or will ever exist. You can't get a better specification than one that is executable, functional, testable, deployed and operating 24/7 across the planet.
|
||||
|
||||
There is not, and never will be a separate, derived "formal spec", "RFC" or anything similar for Reticulum:
|
||||
|
||||
**The reference implementation *is* the final, authoritative specification.**
|
||||
|
||||
Compatibility with the Reticulum Protocol is defined as having full interoperability,
|
||||
and sufficient functional parity with this reference implementation. Any specific protocol
|
||||
implementation that achieves this is Reticulum. Any that does not is not Reticulum.
|
||||
|
||||
@@ -16,7 +16,7 @@ No kernel modules or drivers are required. Reticulum runs completely in userland
|
||||
|
||||
The full documentation for Reticulum is available on `_`!`[this node`:/page/blob.mu`g=reticulum|r=reticulum|ref=HEAD|path=docs/markdown/index.md]`!`_.
|
||||
|
||||
You can also download the `_`!`[Reticulum manual as a PDF`:/file/download`g=reticulum|r=reticulum|ref=HEAD|path=docs%2FReticulum+Manual.pdf]`!`_ or `_`!`[as an e-book in EPUB format`:/file/download`g=reticulum|r=reticulum|ref=HEAD|path=docs%2FReticulum+Manual.pdf]`!`_.
|
||||
You can also download the `_`!`[Reticulum manual as a PDF`:/file/artifact`g=reticulum|r=reticulum|t=latest|a=Reticulum+Manual.pdf]`!`_ or `_`!`[as an e-book in EPUB format`:/file/artifact`g=reticulum|r=reticulum|t=latest|a=Reticulum+Manual.epub]`!`_.
|
||||
|
||||
>> Notable Features
|
||||
|
||||
@@ -58,6 +58,12 @@ You can also download the `_`!`[Reticulum manual as a PDF`:/file/download`g=reti
|
||||
|
||||
The Python code in this repository is the Reference Implementation of Reticulum. The Reticulum Protocol is defined entirely and authoritatively by this reference implementation, and its associated manual. It is maintained by Mark Qvist, identified by the Reticulum Identity `B333<bc7291552be7a58f361522990465165c>`b.
|
||||
|
||||
No second-order protocol specification derivatives currently do, or will ever exist. You can't get a better specification than one that is executable, functional, testable, deployed and operating 24/7 across the planet.
|
||||
|
||||
There is not, and never will be a separate, derived "formal spec", "RFC" or anything similar for Reticulum:
|
||||
|
||||
`!The reference implementation `*is`* the final, authoritative specification.`!
|
||||
|
||||
Compatibility with the Reticulum Protocol is defined as having full interoperability, and sufficient functional parity with this reference implementation. Any specific protocol implementation that achieves this is Reticulum. Any that does not is not Reticulum.
|
||||
|
||||
The reference implementation is licensed under the Reticulum License.
|
||||
|
||||
+12
-8
@@ -316,6 +316,7 @@ class Identity:
|
||||
no_path = 0
|
||||
retained = 0
|
||||
never_used = 0
|
||||
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
|
||||
|
||||
with Identity.known_destinations_lock: destination_hashes = list(Identity.known_destinations.keys())
|
||||
for destination_hash in destination_hashes:
|
||||
@@ -359,6 +360,12 @@ class Identity:
|
||||
Identity.known_destinations.pop(destination_hash)
|
||||
removed += 1
|
||||
|
||||
try:
|
||||
hexhash = RNS.hexrep(destination_hash, delimit=False)
|
||||
ratchet_path = f"{ratchetdir}/{hexhash}"
|
||||
if os.path.isfile(ratchet_path): os.unlink(ratchet_path)
|
||||
except Exception as e: RNS.log(f"Could not clean stale ratchets for {RNS.prettyhexrep(destination_hash)}: {e}", RNS.LOG_WARNING)
|
||||
|
||||
# RNS.log(f"Total destinations: {total}, stale: {len(stale)}, removed: {removed}, no path: {no_path}, never used: {never_used}, with path: {total-no_path}, used: {total-never_used}, retained: {retained}. Completed in {RNS.prettyshorttime(time.time()-st)}", RNS.LOG_WARNING) # TODO: Remove
|
||||
if not RNS.Transport.owner.is_connected_to_shared_instance: Identity.save_known_destinations(recombine=False)
|
||||
|
||||
@@ -595,21 +602,18 @@ class Identity:
|
||||
signal_str = " ["
|
||||
if packet.rssi != None:
|
||||
signal_str += "RSSI "+str(packet.rssi)+"dBm"
|
||||
if packet.snr != None:
|
||||
signal_str += ", "
|
||||
if packet.snr != None:
|
||||
signal_str += "SNR "+str(packet.snr)+"dB"
|
||||
if packet.snr != None: signal_str += ", "
|
||||
if packet.snr != None: signal_str += "SNR "+str(packet.snr)+"dB"
|
||||
signal_str += "]"
|
||||
else:
|
||||
signal_str = ""
|
||||
|
||||
else: signal_str = ""
|
||||
|
||||
if hasattr(packet, "transport_id") and packet.transport_id != None:
|
||||
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received via "+RNS.prettyhexrep(packet.transport_id)+" on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
else:
|
||||
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
|
||||
if ratchet:
|
||||
Identity._remember_ratchet(destination_hash, ratchet)
|
||||
if ratchet: Identity._remember_ratchet(destination_hash, ratchet)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -428,14 +428,22 @@ class AutoInterface(Interface):
|
||||
if ifname in self.interface_servers:
|
||||
RNS.log("Shutting down previous UDP listener for "+str(self)+" "+str(ifname), RNS.LOG_DEBUG)
|
||||
previous_server = self.interface_servers[ifname]
|
||||
def shutdown_server():
|
||||
previous_server.shutdown()
|
||||
def shutdown_server(): previous_server.shutdown()
|
||||
threading.Thread(target=shutdown_server, daemon=True).start()
|
||||
|
||||
RNS.log("Starting new UDP listener for "+str(self)+" "+str(ifname), RNS.LOG_DEBUG)
|
||||
|
||||
udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.process_incoming))
|
||||
self.interface_servers[ifname] = udp_server
|
||||
retry_delay = 1.25
|
||||
listener_started = False
|
||||
while not listener_started:
|
||||
try:
|
||||
time.sleep(retry_delay)
|
||||
udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.process_incoming))
|
||||
self.interface_servers[ifname] = udp_server
|
||||
listener_started = True
|
||||
except Exception as e:
|
||||
RNS.log(f"Could not start new UDP listener for {self} on {listen_address}: {e}", RNS.LOG_WARNING)
|
||||
RNS.log(f"Retrying in {retry_delay} seconds", RNS.LOG_WARNING)
|
||||
|
||||
thread = threading.Thread(target=udp_server.serve_forever)
|
||||
thread.daemon = True
|
||||
|
||||
+18
-24
@@ -48,10 +48,11 @@ class Interface:
|
||||
MODE_ROAMING = 0x04
|
||||
MODE_BOUNDARY = 0x05
|
||||
MODE_GATEWAY = 0x06
|
||||
MODE_INTERNAL = 0x07
|
||||
|
||||
# Which interface modes a Transport Node should
|
||||
# actively discover paths for.
|
||||
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY, MODE_ROAMING]
|
||||
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY, MODE_ROAMING, MODE_INTERNAL]
|
||||
|
||||
# How many samples to use for announce
|
||||
# frequency calculations
|
||||
@@ -106,6 +107,8 @@ class Interface:
|
||||
self.discoverable = False
|
||||
self.last_discovery_announce = 0
|
||||
self.bootstrap_only = False
|
||||
self.recursive_prs = False
|
||||
self.announces_from_internal = True
|
||||
self.parent_interface = None
|
||||
self.spawned_interfaces = None
|
||||
self.tunnel_id = None
|
||||
@@ -197,28 +200,17 @@ class Interface:
|
||||
|
||||
def optimise_mtu(self):
|
||||
if self.AUTOCONFIGURE_MTU:
|
||||
if self.bitrate >= 1_000_000_000:
|
||||
self.HW_MTU = 524288
|
||||
elif self.bitrate > 750_000_000:
|
||||
self.HW_MTU = 262144
|
||||
elif self.bitrate > 400_000_000:
|
||||
self.HW_MTU = 131072
|
||||
elif self.bitrate > 200_000_000:
|
||||
self.HW_MTU = 65536
|
||||
elif self.bitrate > 100_000_000:
|
||||
self.HW_MTU = 32768
|
||||
elif self.bitrate > 10_000_000:
|
||||
self.HW_MTU = 16384
|
||||
elif self.bitrate > 5_000_000:
|
||||
self.HW_MTU = 8192
|
||||
elif self.bitrate > 2_000_000:
|
||||
self.HW_MTU = 4096
|
||||
elif self.bitrate > 1_000_000:
|
||||
self.HW_MTU = 2048
|
||||
elif self.bitrate > 62_500:
|
||||
self.HW_MTU = 1024
|
||||
else:
|
||||
self.HW_MTU = None
|
||||
if self.bitrate >= 1_000_000_000: self.HW_MTU = 524288
|
||||
elif self.bitrate > 750_000_000: self.HW_MTU = 262144
|
||||
elif self.bitrate > 400_000_000: self.HW_MTU = 131072
|
||||
elif self.bitrate > 200_000_000: self.HW_MTU = 65536
|
||||
elif self.bitrate > 100_000_000: self.HW_MTU = 32768
|
||||
elif self.bitrate > 10_000_000: self.HW_MTU = 16384
|
||||
elif self.bitrate > 5_000_000: self.HW_MTU = 8192
|
||||
elif self.bitrate > 2_000_000: self.HW_MTU = 4096
|
||||
elif self.bitrate > 1_000_000: self.HW_MTU = 2048
|
||||
elif self.bitrate > 62_500: self.HW_MTU = 1024
|
||||
else: self.HW_MTU = None
|
||||
|
||||
RNS.log(f"{self} hardware MTU set to {self.HW_MTU}", RNS.LOG_DEBUG)
|
||||
|
||||
@@ -226,7 +218,9 @@ class Interface:
|
||||
return time.time()-self.created
|
||||
|
||||
def hold_announce(self, announce_packet):
|
||||
if announce_packet.destination_hash in self.held_announces:
|
||||
if announce_packet.hops >= RNS.Transport.PATHFINDER_M-1:
|
||||
return
|
||||
elif 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
|
||||
|
||||
+110
-128
@@ -109,30 +109,30 @@ class WDCL():
|
||||
self.switch_id = self.switch_identity.sig_pub_bytes[-4:]
|
||||
self.switch_pub_bytes = self.switch_identity.sig_pub_bytes
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
self.owner = owner
|
||||
self.as_interface = as_interface
|
||||
self.device = device
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
self.owner = owner
|
||||
self.as_interface = as_interface
|
||||
self.device = device
|
||||
self.device.connection = self
|
||||
self.pyserial = serial
|
||||
self.serial = None
|
||||
self.port = port
|
||||
self.speed = 3000000
|
||||
self.databits = 8
|
||||
self.parity = parity
|
||||
self.stopbits = 1
|
||||
self.timeout = 100
|
||||
self.online = False
|
||||
self.frame_buffer = b""
|
||||
self.next_tx = 0
|
||||
self.should_run = True
|
||||
self.receiver = None
|
||||
self.wdcl_connected = False
|
||||
self.reconnecting = False
|
||||
self.frame_queue = deque()
|
||||
if not self.as_interface:
|
||||
self.id = RNS.Identity.full_hash(port.hwid.encode("utf-8"))
|
||||
self.pyserial = serial
|
||||
self.serial = None
|
||||
self.port = port
|
||||
self.speed = 3000000
|
||||
self.databits = 8
|
||||
self.parity = parity
|
||||
self.stopbits = 1
|
||||
self.timeout = 100
|
||||
self.online = False
|
||||
self.frame_buffer = b""
|
||||
self.next_tx = 0
|
||||
self.should_run = True
|
||||
self.receiver = None
|
||||
self.wdcl_connected = False
|
||||
self.reconnecting = False
|
||||
self.frame_queue = deque()
|
||||
|
||||
if not self.as_interface: self.id = RNS.Identity.full_hash(port.hwid.encode("utf-8"))
|
||||
|
||||
if self.as_interface:
|
||||
try:
|
||||
@@ -168,19 +168,10 @@ class WDCL():
|
||||
self.owner.wlog(f"Opening serial port {self.port.device}...")
|
||||
target_port = self.port.device
|
||||
|
||||
self.serial = self.pyserial.Serial(
|
||||
port = target_port,
|
||||
baudrate = self.speed,
|
||||
bytesize = self.databits,
|
||||
parity = self.parity,
|
||||
stopbits = self.stopbits,
|
||||
xonxoff = False,
|
||||
rtscts = False,
|
||||
timeout = 0.250,
|
||||
inter_byte_timeout = None,
|
||||
write_timeout = None,
|
||||
dsrdtr = False)
|
||||
|
||||
self.serial = self.pyserial.Serial(port = target_port, baudrate = self.speed,
|
||||
bytesize = self.databits, parity = self.parity, stopbits = self.stopbits,
|
||||
xonxoff = False, rtscts = False, timeout = 0.250, inter_byte_timeout = None,
|
||||
write_timeout = None, dsrdtr = False)
|
||||
else:
|
||||
if self.port != None:
|
||||
# Get device parameters
|
||||
@@ -198,19 +189,9 @@ class WDCL():
|
||||
from usbserial4a.cdcacmserial4a import CdcAcmSerial
|
||||
proxy = CdcAcmSerial
|
||||
|
||||
self.serial = proxy(
|
||||
self.port,
|
||||
baudrate = self.speed,
|
||||
bytesize = self.databits,
|
||||
parity = self.parity,
|
||||
stopbits = self.stopbits,
|
||||
xonxoff = False,
|
||||
rtscts = False,
|
||||
timeout = None,
|
||||
inter_byte_timeout = None,
|
||||
# write_timeout = wtimeout,
|
||||
dsrdtr = False,
|
||||
)
|
||||
self.serial = proxy(self.port, baudrate = self.speed, bytesize = self.databits,
|
||||
parity = self.parity, stopbits = self.stopbits, xonxoff = False,
|
||||
rtscts = False, timeout = None, inter_byte_timeout = None, dsrdtr = False)
|
||||
|
||||
if vid == 0x0403:
|
||||
# Hardware parameters for FTDI devices @ 115200 baud
|
||||
@@ -299,10 +280,9 @@ class WDCL():
|
||||
frame = frame.replace(bytes([HDLC.ESC, HDLC.ESC ^ HDLC.ESC_MASK]), bytes([HDLC.ESC]))
|
||||
if len(frame) > WDCL.HEADER_MINSIZE: self.process_incoming(frame)
|
||||
self.frame_buffer = self.frame_buffer[frame_end:]
|
||||
else:
|
||||
flags_remaining = False
|
||||
else:
|
||||
flags_remaining = False
|
||||
|
||||
else: flags_remaining = False
|
||||
else: flags_remaining = False
|
||||
|
||||
except Exception as e:
|
||||
self.online = False
|
||||
@@ -359,6 +339,7 @@ class Evt():
|
||||
ET_MSG = 0x0000
|
||||
ET_SYSTEM_BOOT = 0x0001
|
||||
ET_CORE_INIT = 0x0002
|
||||
ET_BOARD_INIT = 0x0003
|
||||
ET_DRV_UART_INIT = 0x1000
|
||||
ET_DRV_USB_CDC_INIT = 0x1010
|
||||
ET_DRV_USB_CDC_HOST_AVAIL = 0x1011
|
||||
@@ -426,77 +407,78 @@ class Evt():
|
||||
IF_TYPE_DMA = 0x10
|
||||
|
||||
event_descriptions = {
|
||||
ET_SYSTEM_BOOT: "System boot",
|
||||
ET_CORE_INIT: "Core initialization",
|
||||
ET_DRV_UART_INIT: "UART driver initialization",
|
||||
ET_DRV_USB_CDC_INIT: "USB CDC driver initialization",
|
||||
ET_DRV_USB_CDC_HOST_AVAIL: "USB CDC host became available",
|
||||
ET_DRV_USB_CDC_HOST_SUSPEND: "USB CDC host suspend",
|
||||
ET_DRV_USB_CDC_HOST_RESUME: "USB CDC host resume",
|
||||
ET_DRV_USB_CDC_CONNECTED: "USB CDC host connection",
|
||||
ET_DRV_USB_CDC_READ_ERR: "USB CDC read error",
|
||||
ET_DRV_USB_CDC_OVERFLOW: "USB CDC overflow occurred",
|
||||
ET_DRV_USB_CDC_DROPPED: "USB CDC dropped bytes",
|
||||
ET_DRV_USB_CDC_TX_TIMEOUT: "USB CDC TX flush timeout",
|
||||
ET_DRV_I2C_INIT: "I2C driver initialization",
|
||||
ET_DRV_NVS_INIT: "NVS driver initialization",
|
||||
ET_DRV_CRYPTO_INIT: "Cryptography driver initialization",
|
||||
ET_DRV_W80211_INIT: "W802.11 driver initialization",
|
||||
ET_DRV_W80211_CHANNEL: "W802.11 channel configuration",
|
||||
ET_DRV_W80211_POWER: "W802.11 TX power configuration",
|
||||
ET_DRV_DISPLAY_INIT: "Display driver initialization",
|
||||
ET_SYSTEM_BOOT: "System boot",
|
||||
ET_CORE_INIT: "Core initialization",
|
||||
ET_BOARD_INIT: "Board hardware initialization",
|
||||
ET_DRV_UART_INIT: "UART driver initialization",
|
||||
ET_DRV_USB_CDC_INIT: "USB CDC driver initialization",
|
||||
ET_DRV_USB_CDC_HOST_AVAIL: "USB CDC host became available",
|
||||
ET_DRV_USB_CDC_HOST_SUSPEND: "USB CDC host suspend",
|
||||
ET_DRV_USB_CDC_HOST_RESUME: "USB CDC host resume",
|
||||
ET_DRV_USB_CDC_CONNECTED: "USB CDC host connection",
|
||||
ET_DRV_USB_CDC_READ_ERR: "USB CDC read error",
|
||||
ET_DRV_USB_CDC_OVERFLOW: "USB CDC overflow occurred",
|
||||
ET_DRV_USB_CDC_DROPPED: "USB CDC dropped bytes",
|
||||
ET_DRV_USB_CDC_TX_TIMEOUT: "USB CDC TX flush timeout",
|
||||
ET_DRV_I2C_INIT: "I2C driver initialization",
|
||||
ET_DRV_NVS_INIT: "NVS driver initialization",
|
||||
ET_DRV_CRYPTO_INIT: "Cryptography driver initialization",
|
||||
ET_DRV_W80211_INIT: "W802.11 driver initialization",
|
||||
ET_DRV_W80211_CHANNEL: "W802.11 channel configuration",
|
||||
ET_DRV_W80211_POWER: "W802.11 TX power configuration",
|
||||
ET_DRV_DISPLAY_INIT: "Display driver initialization",
|
||||
ET_DRV_DISPLAY_BUS_AVAILABLE: "Display bus availability",
|
||||
ET_DRV_DISPLAY_IO_CONFIGURED: "Display I/O configuration",
|
||||
ET_DRV_DISPLAY_PANEL_CREATED: "Display panel allocation",
|
||||
ET_DRV_DISPLAY_PANEL_RESET: "Display panel reset",
|
||||
ET_DRV_DISPLAY_PANEL_INIT: "Display panel initialization",
|
||||
ET_DRV_DISPLAY_PANEL_ENABLE: "Display panel activation",
|
||||
ET_DRV_DISPLAY_PANEL_RESET: "Display panel reset",
|
||||
ET_DRV_DISPLAY_PANEL_INIT: "Display panel initialization",
|
||||
ET_DRV_DISPLAY_PANEL_ENABLE: "Display panel activation",
|
||||
ET_DRV_DISPLAY_REMOTE_ENABLE: "Remote display output activation",
|
||||
ET_KRN_LOGGER_INIT: "Logging service initialization",
|
||||
ET_KRN_LOGGER_OUTPUT: "Logging service output activation",
|
||||
ET_KRN_UI_INIT: "User interface service initialization",
|
||||
ET_PROTO_WDCL_INIT: "WDCL protocol initialization",
|
||||
ET_PROTO_WDCL_RUNNING: "WDCL protocol activation",
|
||||
ET_PROTO_WDCL_CONNECTION: "WDCL host connection",
|
||||
ET_PROTO_WDCL_HOST_ENDPOINT: "Weave host endpoint",
|
||||
ET_PROTO_WEAVE_INIT: "Weave protocol initialization",
|
||||
ET_PROTO_WEAVE_RUNNING: "Weave protocol activation",
|
||||
ET_PROTO_WEAVE_EP_ALIVE: "Weave endpoint alive",
|
||||
ET_PROTO_WEAVE_EP_TIMEOUT: "Weave endpoint disappeared",
|
||||
ET_SRVCTL_REMOTE_DISPLAY: "Remote display service control event",
|
||||
ET_INTERFACE_REGISTERED: "Interface registration",
|
||||
ET_SYSERR_MEM_EXHAUSTED: "System memory exhausted",
|
||||
ET_KRN_LOGGER_INIT: "Logging service initialization",
|
||||
ET_KRN_LOGGER_OUTPUT: "Logging service output activation",
|
||||
ET_KRN_UI_INIT: "User interface service initialization",
|
||||
ET_PROTO_WDCL_INIT: "WDCL protocol initialization",
|
||||
ET_PROTO_WDCL_RUNNING: "WDCL protocol activation",
|
||||
ET_PROTO_WDCL_CONNECTION: "WDCL host connection",
|
||||
ET_PROTO_WDCL_HOST_ENDPOINT: "Weave host endpoint",
|
||||
ET_PROTO_WEAVE_INIT: "Weave protocol initialization",
|
||||
ET_PROTO_WEAVE_RUNNING: "Weave protocol activation",
|
||||
ET_PROTO_WEAVE_EP_ALIVE: "Weave endpoint alive",
|
||||
ET_PROTO_WEAVE_EP_TIMEOUT: "Weave endpoint disappeared",
|
||||
ET_SRVCTL_REMOTE_DISPLAY: "Remote display service control event",
|
||||
ET_INTERFACE_REGISTERED: "Interface registration",
|
||||
ET_SYSERR_MEM_EXHAUSTED: "System memory exhausted",
|
||||
}
|
||||
|
||||
interface_types = {
|
||||
IF_TYPE_USB: "usb",
|
||||
IF_TYPE_UART: "uart",
|
||||
IF_TYPE_W80211: "mw",
|
||||
IF_TYPE_BLE: "ble",
|
||||
IF_TYPE_LORA: "lora",
|
||||
IF_TYPE_USB: "usb",
|
||||
IF_TYPE_UART: "uart",
|
||||
IF_TYPE_W80211: "mw",
|
||||
IF_TYPE_BLE: "ble",
|
||||
IF_TYPE_LORA: "lora",
|
||||
IF_TYPE_ETHERNET: "eth",
|
||||
IF_TYPE_WIFI: "wifi",
|
||||
IF_TYPE_TCP: "tcp",
|
||||
IF_TYPE_UDP: "udp",
|
||||
IF_TYPE_IR: "ir",
|
||||
IF_TYPE_AFSK: "afsk",
|
||||
IF_TYPE_GPIO: "gpio",
|
||||
IF_TYPE_SPI: "spi",
|
||||
IF_TYPE_I2C: "i2c",
|
||||
IF_TYPE_CAN: "can",
|
||||
IF_TYPE_DMA: "dma",
|
||||
IF_TYPE_WIFI: "wifi",
|
||||
IF_TYPE_TCP: "tcp",
|
||||
IF_TYPE_UDP: "udp",
|
||||
IF_TYPE_IR: "ir",
|
||||
IF_TYPE_AFSK: "afsk",
|
||||
IF_TYPE_GPIO: "gpio",
|
||||
IF_TYPE_SPI: "spi",
|
||||
IF_TYPE_I2C: "i2c",
|
||||
IF_TYPE_CAN: "can",
|
||||
IF_TYPE_DMA: "dma",
|
||||
}
|
||||
|
||||
channel_descriptions = {
|
||||
1: "Channel 1 (2412 MHz)",
|
||||
2: "Channel 2 (2417 MHz)",
|
||||
3: "Channel 3 (2422 MHz)",
|
||||
4: "Channel 4 (2427 MHz)",
|
||||
5: "Channel 5 (2432 MHz)",
|
||||
6: "Channel 6 (2437 MHz)",
|
||||
7: "Channel 7 (2442 MHz)",
|
||||
8: "Channel 8 (2447 MHz)",
|
||||
9: "Channel 9 (2452 MHz)",
|
||||
1: "Channel 1 (2412 MHz)",
|
||||
2: "Channel 2 (2417 MHz)",
|
||||
3: "Channel 3 (2422 MHz)",
|
||||
4: "Channel 4 (2427 MHz)",
|
||||
5: "Channel 5 (2432 MHz)",
|
||||
6: "Channel 6 (2437 MHz)",
|
||||
7: "Channel 7 (2442 MHz)",
|
||||
8: "Channel 8 (2447 MHz)",
|
||||
9: "Channel 9 (2452 MHz)",
|
||||
10: "Channel 10 (2457 MHz)",
|
||||
11: "Channel 11 (2462 MHz)",
|
||||
12: "Channel 12 (2467 MHz)",
|
||||
@@ -529,23 +511,23 @@ class Evt():
|
||||
}
|
||||
|
||||
task_descriptions = {
|
||||
"taskLVGL": "Driver: UI Renderer",
|
||||
"ui_service": "Service: User Interface",
|
||||
"TinyUSB": "Driver: USB",
|
||||
"drv_w80211": "Driver: W802.11",
|
||||
"system_stats": "System: Stats",
|
||||
"core": "System: Core",
|
||||
"protocol_wdcl": "Protocol: WDCL",
|
||||
"taskLVGL": "Driver: UI Renderer",
|
||||
"ui_service": "Service: User Interface",
|
||||
"TinyUSB": "Driver: USB",
|
||||
"drv_w80211": "Driver: W802.11",
|
||||
"system_stats": "System: Stats",
|
||||
"core": "System: Core",
|
||||
"protocol_wdcl": "Protocol: WDCL",
|
||||
"protocol_weave": "Protocol: Weave",
|
||||
"tiT": "Protocol: TCP/IP",
|
||||
"ipc0": "System: CPU 0 IPC",
|
||||
"ipc1": "System: CPU 1 IPC",
|
||||
"esp_timer": "Driver: Timers",
|
||||
"Tmr Svc": "Service: Timers",
|
||||
"kernel_logger": "Service: Logging",
|
||||
"tiT": "Protocol: TCP/IP",
|
||||
"ipc0": "System: CPU 0 IPC",
|
||||
"ipc1": "System: CPU 1 IPC",
|
||||
"esp_timer": "Driver: Timers",
|
||||
"Tmr Svc": "Service: Timers",
|
||||
"kernel_logger": "Service: Logging",
|
||||
"remote_display": "Service: Remote Display",
|
||||
"wifi": "System: WiFi Hardware",
|
||||
"sys_evt": "System: Kernel Events",
|
||||
"wifi": "System: WiFi Hardware",
|
||||
"sys_evt": "System: Kernel Events",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
||||
+73
-152
@@ -191,8 +191,7 @@ class Link:
|
||||
|
||||
if len(data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE:
|
||||
RNS.log("Link request includes MTU signalling", RNS.LOG_DEBUG) # TODO: Remove debug
|
||||
try:
|
||||
link.mtu = Link.mtu_from_lr_packet(packet) or Reticulum.MTU
|
||||
try: link.mtu = Link.mtu_from_lr_packet(packet) or Reticulum.MTU
|
||||
except Exception as e:
|
||||
RNS.trace_exception(e)
|
||||
link.mtu = RNS.Reticulum.MTU
|
||||
@@ -277,6 +276,7 @@ class Link:
|
||||
self.initiator = False
|
||||
self.prv = X25519PrivateKey.generate()
|
||||
self.sig_prv = self.owner.identity.sig_prv
|
||||
|
||||
else:
|
||||
self.initiator = True
|
||||
self.expected_hops = RNS.Transport.hops_to(self.destination.hash)
|
||||
@@ -296,14 +296,11 @@ class Link:
|
||||
if peer_pub_bytes == None:
|
||||
self.peer_pub = None
|
||||
self.peer_pub_bytes = None
|
||||
else:
|
||||
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
|
||||
|
||||
if established_callback != None:
|
||||
self.set_link_established_callback(established_callback)
|
||||
else: self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
|
||||
|
||||
if closed_callback != None:
|
||||
self.set_link_closed_callback(closed_callback)
|
||||
if established_callback != None: self.set_link_established_callback(established_callback)
|
||||
if closed_callback != None: self.set_link_closed_callback(closed_callback)
|
||||
|
||||
if self.initiator:
|
||||
signalling_bytes = b""
|
||||
@@ -334,8 +331,7 @@ class Link:
|
||||
self.peer_sig_pub_bytes = peer_sig_pub_bytes
|
||||
self.peer_sig_pub = Ed25519PublicKey.from_public_bytes(self.peer_sig_pub_bytes)
|
||||
|
||||
if not hasattr(self.peer_pub, "curve"):
|
||||
self.peer_pub.curve = Link.CURVE
|
||||
if not hasattr(self.peer_pub, "curve"): self.peer_pub.curve = Link.CURVE
|
||||
|
||||
@staticmethod
|
||||
def link_id_from_lr_packet(packet):
|
||||
@@ -447,8 +443,8 @@ class Link:
|
||||
thread = threading.Thread(target=self.callbacks.link_established, args=(self,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
else:
|
||||
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
|
||||
|
||||
else: RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
self.status = Link.CLOSED
|
||||
@@ -497,35 +493,21 @@ class Link:
|
||||
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
|
||||
packet_receipt = request_packet.send()
|
||||
|
||||
if packet_receipt == False:
|
||||
return False
|
||||
if packet_receipt == False: return False
|
||||
else:
|
||||
packet_receipt.set_timeout(timeout)
|
||||
return RequestReceipt(
|
||||
self,
|
||||
packet_receipt = packet_receipt,
|
||||
response_callback = response_callback,
|
||||
failed_callback = failed_callback,
|
||||
progress_callback = progress_callback,
|
||||
timeout = timeout,
|
||||
request_size = len(packed_request),
|
||||
)
|
||||
return RequestReceipt(self, packet_receipt = packet_receipt, response_callback = response_callback,
|
||||
failed_callback = failed_callback, progress_callback = progress_callback,
|
||||
timeout = timeout, request_size = len(packed_request))
|
||||
|
||||
else:
|
||||
request_id = RNS.Identity.truncated_hash(packed_request)
|
||||
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG)
|
||||
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False, timeout = timeout)
|
||||
|
||||
return RequestReceipt(
|
||||
self,
|
||||
resource = request_resource,
|
||||
response_callback = response_callback,
|
||||
failed_callback = failed_callback,
|
||||
progress_callback = progress_callback,
|
||||
timeout = timeout,
|
||||
request_size = len(packed_request),
|
||||
)
|
||||
|
||||
return RequestReceipt(self, resource = request_resource, response_callback = response_callback,
|
||||
failed_callback = failed_callback, progress_callback = progress_callback,
|
||||
timeout = timeout, request_size = len(packed_request))
|
||||
|
||||
def update_mdu(self):
|
||||
self.mdu = self.mtu - RNS.Reticulum.HEADER_MAXSIZE - RNS.Reticulum.IFAC_MIN_SIZE
|
||||
@@ -540,6 +522,7 @@ class Link:
|
||||
self.rtt = max(measured_rtt, rtt)
|
||||
self.status = Link.ACTIVE
|
||||
self.activated_at = time.time()
|
||||
self.expected_hops = packet.hops
|
||||
|
||||
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
|
||||
@@ -547,10 +530,8 @@ class Link:
|
||||
self.__update_keepalive()
|
||||
|
||||
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)
|
||||
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)
|
||||
@@ -565,73 +546,57 @@ class 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
|
||||
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.
|
||||
"""
|
||||
if self.__track_phy_stats:
|
||||
return self.rssi
|
||||
else:
|
||||
return None
|
||||
if self.__track_phy_stats: return self.rssi
|
||||
else: return None
|
||||
|
||||
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.
|
||||
"""
|
||||
if self.__track_phy_stats:
|
||||
return self.snr
|
||||
else:
|
||||
return None
|
||||
if self.__track_phy_stats: return self.snr
|
||||
else: return None
|
||||
|
||||
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.
|
||||
"""
|
||||
if self.__track_phy_stats:
|
||||
return self.q
|
||||
else:
|
||||
return None
|
||||
if self.__track_phy_stats: return self.q
|
||||
else: return None
|
||||
|
||||
def get_establishment_rate(self):
|
||||
"""
|
||||
:returns: The data transfer rate at which the link establishment procedure ocurred, in bits per second.
|
||||
"""
|
||||
if self.establishment_rate != None:
|
||||
return self.establishment_rate*8
|
||||
else:
|
||||
return None
|
||||
if self.establishment_rate != None: return self.establishment_rate*8
|
||||
else: return None
|
||||
|
||||
def get_mtu(self):
|
||||
"""
|
||||
:returns: The MTU of an established link.
|
||||
"""
|
||||
if self.status == Link.ACTIVE:
|
||||
return self.mtu
|
||||
else:
|
||||
return None
|
||||
if self.status == Link.ACTIVE: return self.mtu
|
||||
else: return None
|
||||
|
||||
def get_mdu(self):
|
||||
"""
|
||||
:returns: The packet MDU of an established link.
|
||||
"""
|
||||
if self.status == Link.ACTIVE:
|
||||
return self.mdu
|
||||
else:
|
||||
return None
|
||||
if self.status == Link.ACTIVE: return self.mdu
|
||||
else: return None
|
||||
|
||||
def get_expected_rate(self):
|
||||
"""
|
||||
:returns: The packet expected in-flight data rate of an established link.
|
||||
"""
|
||||
if self.status == Link.ACTIVE:
|
||||
return self.expected_rate
|
||||
else:
|
||||
return None
|
||||
if self.status == Link.ACTIVE: return self.expected_rate
|
||||
else: return None
|
||||
|
||||
def get_mode(self):
|
||||
"""
|
||||
@@ -649,10 +614,8 @@ class Link:
|
||||
"""
|
||||
:returns: The time in seconds since this link was established.
|
||||
"""
|
||||
if self.activated_at:
|
||||
return time.time() - self.activated_at
|
||||
else:
|
||||
return None
|
||||
if self.activated_at: return time.time() - self.activated_at
|
||||
else: return None
|
||||
|
||||
def no_inbound_for(self):
|
||||
"""
|
||||
@@ -712,14 +675,11 @@ class Link:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
if plaintext == self.link_id:
|
||||
self.status = Link.CLOSED
|
||||
if self.initiator:
|
||||
self.teardown_reason = Link.DESTINATION_CLOSED
|
||||
else:
|
||||
self.teardown_reason = Link.INITIATOR_CLOSED
|
||||
if self.initiator: 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
|
||||
except Exception as e: pass
|
||||
|
||||
def link_closed(self):
|
||||
for resource in self.incoming_resources: resource.cancel()
|
||||
@@ -752,9 +712,7 @@ class Link:
|
||||
while not self.status == Link.CLOSED:
|
||||
while (self.watchdog_lock):
|
||||
rtt_wait = 0.025
|
||||
if hasattr(self, "rtt") and self.rtt:
|
||||
rtt_wait = self.rtt
|
||||
|
||||
if hasattr(self, "rtt") and self.rtt: rtt_wait = self.rtt
|
||||
sleep(max(rtt_wait, 0.025))
|
||||
|
||||
if not self.status == Link.CLOSED:
|
||||
@@ -779,10 +737,8 @@ class Link:
|
||||
self.link_closed()
|
||||
sleep_time = 0.001
|
||||
|
||||
if self.initiator:
|
||||
RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG)
|
||||
if self.initiator: RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG)
|
||||
else: RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG)
|
||||
|
||||
elif self.status == Link.ACTIVE:
|
||||
activated_at = self.activated_at if self.activated_at != None else 0
|
||||
@@ -796,11 +752,10 @@ class Link:
|
||||
if time.time() >= last_inbound + self.stale_time:
|
||||
sleep_time = self.rtt * self.keepalive_timeout_factor + Link.STALE_GRACE
|
||||
self.status = Link.STALE
|
||||
else:
|
||||
sleep_time = self.keepalive
|
||||
|
||||
else: sleep_time = self.keepalive
|
||||
|
||||
else:
|
||||
sleep_time = (last_inbound + self.keepalive) - time.time()
|
||||
else: sleep_time = (last_inbound + self.keepalive) - time.time()
|
||||
|
||||
elif self.status == Link.STALE:
|
||||
sleep_time = 0.001
|
||||
@@ -810,8 +765,7 @@ class Link:
|
||||
self.link_closed()
|
||||
|
||||
|
||||
if sleep_time == 0:
|
||||
RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_ERROR)
|
||||
if sleep_time == 0: RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_ERROR)
|
||||
if sleep_time == None or sleep_time < 0:
|
||||
RNS.log("Timing error! Tearing down link "+str(self)+" now.", RNS.LOG_ERROR)
|
||||
self.teardown()
|
||||
@@ -825,7 +779,6 @@ class Link:
|
||||
self.snr = None
|
||||
self.q = None
|
||||
|
||||
|
||||
def __update_phy_stats(self, packet, query_shared = True, force_update = False):
|
||||
if self.__track_phy_stats or force_update:
|
||||
if query_shared:
|
||||
@@ -834,12 +787,9 @@ class Link:
|
||||
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
|
||||
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 __update_keepalive(self):
|
||||
self.keepalive = max(min(self.rtt*(Link.KEEPALIVE_MAX/Link.KEEPALIVE_MAX_RTT), Link.KEEPALIVE_MAX), Link.KEEPALIVE_MIN)
|
||||
@@ -933,8 +883,8 @@ class Link:
|
||||
|
||||
def job(): self.handle_request(request_id, request_data)
|
||||
threading.Thread(target=job, daemon=True).start()
|
||||
else:
|
||||
RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
|
||||
|
||||
else: RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
|
||||
|
||||
def response_resource_concluded(self, resource):
|
||||
if resource.status == RNS.Resource.COMPLETE:
|
||||
@@ -965,8 +915,7 @@ class Link:
|
||||
|
||||
:return: ``Channel`` object
|
||||
"""
|
||||
if self._channel is None:
|
||||
self._channel = Channel(LinkChannelOutlet(self))
|
||||
if self._channel is None: self._channel = Channel(LinkChannelOutlet(self))
|
||||
return self._channel
|
||||
|
||||
def receive(self, packet):
|
||||
@@ -976,12 +925,10 @@ class Link:
|
||||
RNS.log(f"Link-associated packet received on unexpected interface {packet.receiving_interface} instead of {self.attached_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
|
||||
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 self.status == Link.STALE: self.status = Link.ACTIVE
|
||||
|
||||
if packet.packet_type == RNS.Packet.DATA:
|
||||
should_query = False
|
||||
@@ -1002,10 +949,8 @@ class Link:
|
||||
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()
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
if self.destination.callbacks.proof_requested(packet): packet.prove()
|
||||
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 packet.context == RNS.Packet.LINKIDENTIFY:
|
||||
plaintext = self.decrypt(packet.data)
|
||||
@@ -1039,8 +984,7 @@ class Link:
|
||||
def job(): self.handle_request(request_id, unpacked_request)
|
||||
threading.Thread(target=job, daemon=True).start()
|
||||
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)
|
||||
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:
|
||||
@@ -1053,8 +997,7 @@ class Link:
|
||||
def job(): self.handle_response(request_id, response_data, transfer_size, transfer_size, update_sizes=True)
|
||||
threading.Thread(target=job, daemon=True).start()
|
||||
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)
|
||||
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:
|
||||
@@ -1186,7 +1129,6 @@ class Link:
|
||||
|
||||
self.watchdog_lock = False
|
||||
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
try:
|
||||
if not self.token:
|
||||
@@ -1201,7 +1143,6 @@ class Link:
|
||||
RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
raise e
|
||||
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
try:
|
||||
if not self.token: self.token = Token(self.derived_key)
|
||||
@@ -1211,7 +1152,6 @@ class Link:
|
||||
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
|
||||
def sign(self, message):
|
||||
return self.sig_prv.sign(message)
|
||||
|
||||
@@ -1219,8 +1159,7 @@ class Link:
|
||||
try:
|
||||
self.peer_sig_pub.verify(signature, message)
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
except Exception as e: return False
|
||||
|
||||
def set_link_established_callback(self, callback):
|
||||
self.callbacks.link_established = callback
|
||||
@@ -1388,7 +1327,6 @@ class RequestReceipt():
|
||||
|
||||
self.link.pending_requests.append(self)
|
||||
|
||||
|
||||
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)
|
||||
@@ -1411,7 +1349,6 @@ class RequestReceipt():
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing request failed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
|
||||
def __response_timeout_job(self):
|
||||
while self.status == RequestReceipt.DELIVERED:
|
||||
now = time.time()
|
||||
@@ -1421,7 +1358,6 @@ class RequestReceipt():
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def request_timed_out(self, packet_receipt):
|
||||
if self in self.link.pending_requests and self.status == RequestReceipt.DELIVERED:
|
||||
self.status = RequestReceipt.FAILED
|
||||
@@ -1430,9 +1366,7 @@ class RequestReceipt():
|
||||
|
||||
if self.callbacks.failed != None:
|
||||
try: self.callbacks.failed(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
except Exception as e: RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
def response_resource_progress(self, resource):
|
||||
if resource != None:
|
||||
@@ -1449,13 +1383,10 @@ class RequestReceipt():
|
||||
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()
|
||||
|
||||
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, metadata=None):
|
||||
if not self.status == RequestReceipt.FAILED:
|
||||
@@ -1474,13 +1405,11 @@ class RequestReceipt():
|
||||
|
||||
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)
|
||||
except Exception as e: RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
if self.callbacks.response != None:
|
||||
try: self.callbacks.response(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing response received callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
except Exception as e: RNS.log("Error while executing response received callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
def get_request_id(self):
|
||||
"""
|
||||
@@ -1504,31 +1433,23 @@ class RequestReceipt():
|
||||
"""
|
||||
:returns: The response as *bytes* if it is ready, otherwise *None*.
|
||||
"""
|
||||
if self.status == RequestReceipt.READY:
|
||||
return self.response
|
||||
else:
|
||||
return None
|
||||
if self.status == RequestReceipt.READY: return self.response
|
||||
else: return None
|
||||
|
||||
def get_response_time(self):
|
||||
"""
|
||||
:returns: The response time of the request in seconds.
|
||||
"""
|
||||
if self.status == RequestReceipt.READY:
|
||||
return self.response_concluded_at - self.started_at
|
||||
else:
|
||||
return None
|
||||
if self.status == RequestReceipt.READY: return self.response_concluded_at - self.started_at
|
||||
else: return None
|
||||
|
||||
def concluded(self):
|
||||
"""
|
||||
:returns: True if the associated request has concluded (successfully or with a failure), otherwise False.
|
||||
"""
|
||||
if self.status == RequestReceipt.READY:
|
||||
return True
|
||||
elif self.status == RequestReceipt.FAILED:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if self.status == RequestReceipt.READY: return True
|
||||
elif self.status == RequestReceipt.FAILED: return True
|
||||
else: return False
|
||||
|
||||
|
||||
class RequestReceiptCallbacks:
|
||||
|
||||
+46
-73
@@ -244,6 +244,9 @@ class Packet:
|
||||
self.flags = self.raw[0]
|
||||
self.hops = self.raw[1]
|
||||
|
||||
if self.hops >= RNS.Transport.PATHFINDER_M:
|
||||
raise ValueError(f"Invalid hop count {self.hops}")
|
||||
|
||||
self.header_type = (self.flags & 0b01000000) >> 6
|
||||
self.context_flag = (self.flags & 0b00100000) >> 5
|
||||
self.transport_type = (self.flags & 0b00010000) >> 4
|
||||
@@ -268,7 +271,7 @@ class Packet:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Received malformed packet, dropping it. The contained exception was: "+str(e), RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
RNS.log(f"Received malformed packet, dropping it. The contained exception was: {e}", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
return False
|
||||
|
||||
def send(self):
|
||||
@@ -278,6 +281,7 @@ class Packet:
|
||||
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
||||
"""
|
||||
if not self.sent:
|
||||
if not self.packed: self.pack()
|
||||
if self.destination.type == RNS.Destination.LINK:
|
||||
if self.destination.status == RNS.Link.CLOSED:
|
||||
RNS.log("Attempt to transmit over a closed link, dropping packet", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
|
||||
@@ -288,9 +292,7 @@ class Packet:
|
||||
else:
|
||||
self.destination.last_outbound = time.time()
|
||||
self.destination.tx += 1
|
||||
self.destination.txbytes += len(self.data)
|
||||
|
||||
if not self.packed: self.pack()
|
||||
self.destination.txbytes += len(self.ciphertext)
|
||||
|
||||
if RNS.Transport.outbound(self): return self.receipt
|
||||
else:
|
||||
@@ -313,51 +315,40 @@ class Packet:
|
||||
# encrypted destinations
|
||||
self.pack()
|
||||
|
||||
if RNS.Transport.outbound(self):
|
||||
return self.receipt
|
||||
if RNS.Transport.outbound(self): return self.receipt
|
||||
else:
|
||||
RNS.log("Re-send failed. No interfaces could process the outbound packet", RNS.LOG_WARNING)
|
||||
self.sent = False
|
||||
self.receipt = None
|
||||
return False
|
||||
else:
|
||||
raise IOError("Packet was not sent yet")
|
||||
|
||||
else: raise IOError("Packet was not sent yet")
|
||||
|
||||
def prove(self, destination=None):
|
||||
if self.fromPacked and hasattr(self, "destination") and self.destination:
|
||||
if self.destination.identity and self.destination.identity.prv:
|
||||
self.destination.identity.prove(self, destination)
|
||||
elif self.fromPacked and hasattr(self, "link") and self.link:
|
||||
self.link.prove_packet(self)
|
||||
else:
|
||||
RNS.log("Could not prove packet associated with neither a destination nor a link", RNS.LOG_ERROR)
|
||||
elif self.fromPacked and hasattr(self, "link") and self.link: self.link.prove_packet(self)
|
||||
else: RNS.log("Could not prove packet associated with neither a destination nor a link", RNS.LOG_ERROR)
|
||||
|
||||
# Generates a special destination that allows Reticulum
|
||||
# to direct the proof back to the proved packet's sender
|
||||
def generate_proof_destination(self):
|
||||
return ProofDestination(self)
|
||||
def generate_proof_destination(self): return ProofDestination(self)
|
||||
|
||||
def validate_proof_packet(self, proof_packet):
|
||||
return self.receipt.validate_proof_packet(proof_packet)
|
||||
def validate_proof_packet(self, proof_packet): return self.receipt.validate_proof_packet(proof_packet)
|
||||
|
||||
def validate_proof(self, proof):
|
||||
return self.receipt.validate_proof(proof)
|
||||
def validate_proof(self, proof): return self.receipt.validate_proof(proof)
|
||||
|
||||
def update_hash(self):
|
||||
self.packet_hash = self.get_hash()
|
||||
def update_hash(self): self.packet_hash = self.get_hash()
|
||||
|
||||
def get_hash(self):
|
||||
return RNS.Identity.full_hash(self.get_hashable_part())
|
||||
def get_hash(self): return RNS.Identity.full_hash(self.get_hashable_part())
|
||||
|
||||
def getTruncatedHash(self):
|
||||
return RNS.Identity.truncated_hash(self.get_hashable_part())
|
||||
def getTruncatedHash(self): return RNS.Identity.truncated_hash(self.get_hashable_part())
|
||||
|
||||
def get_hashable_part(self):
|
||||
hashable_part = bytes([self.raw[0] & 0b00001111])
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
hashable_part += self.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
|
||||
else:
|
||||
hashable_part += self.raw[2:]
|
||||
if self.header_type == Packet.HEADER_2: hashable_part += self.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
|
||||
else: hashable_part += self.raw[2:]
|
||||
|
||||
return hashable_part
|
||||
|
||||
@@ -365,36 +356,29 @@ class Packet:
|
||||
"""
|
||||
:returns: The physical layer *Received Signal Strength Indication* if available, otherwise ``None``.
|
||||
"""
|
||||
if self.rssi != None:
|
||||
return self.rssi
|
||||
else:
|
||||
return reticulum.get_packet_rssi(self.packet_hash)
|
||||
if self.rssi != None: return self.rssi
|
||||
else: return reticulum.get_packet_rssi(self.packet_hash)
|
||||
|
||||
def get_snr(self):
|
||||
"""
|
||||
:returns: The physical layer *Signal-to-Noise Ratio* if available, otherwise ``None``.
|
||||
"""
|
||||
if self.snr != None:
|
||||
return self.snr
|
||||
else:
|
||||
return reticulum.get_packet_snr(self.packet_hash)
|
||||
if self.snr != None: return self.snr
|
||||
else: return reticulum.get_packet_snr(self.packet_hash)
|
||||
|
||||
def get_q(self):
|
||||
"""
|
||||
:returns: The physical layer *Link Quality* if available, otherwise ``None``.
|
||||
"""
|
||||
if self.q != None:
|
||||
return self.q
|
||||
else:
|
||||
return reticulum.get_packet_q(self.packet_hash)
|
||||
if self.q != None: return self.q
|
||||
else: return reticulum.get_packet_q(self.packet_hash)
|
||||
|
||||
class ProofDestination:
|
||||
def __init__(self, packet):
|
||||
self.hash = packet.get_hash()[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8];
|
||||
self.type = RNS.Destination.SINGLE
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return plaintext
|
||||
def encrypt(self, plaintext): return plaintext
|
||||
|
||||
|
||||
class PacketReceipt:
|
||||
@@ -463,18 +447,17 @@ class PacketReceipt:
|
||||
link.last_proof = self.concluded_at
|
||||
|
||||
if self.callbacks.delivery != None:
|
||||
try:
|
||||
self.callbacks.delivery(self)
|
||||
try: self.callbacks.delivery(self)
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while evaluating external delivery callback for "+str(link), RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
else: return False
|
||||
else: return False
|
||||
|
||||
elif len(proof) == PacketReceipt.IMPL_LENGTH:
|
||||
pass
|
||||
# TODO: Why is this disabled?
|
||||
@@ -491,8 +474,7 @@ class PacketReceipt:
|
||||
# else:
|
||||
# RNS.log("invalid")
|
||||
# return False
|
||||
else:
|
||||
return False
|
||||
else: return False
|
||||
|
||||
# Validate a raw proof
|
||||
def validate_proof(self, proof, proof_packet=None):
|
||||
@@ -509,24 +491,20 @@ class PacketReceipt:
|
||||
self.proof_packet = proof_packet
|
||||
|
||||
if self.callbacks.delivery != None:
|
||||
try:
|
||||
self.callbacks.delivery(self)
|
||||
try: self.callbacks.delivery(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
else: return False
|
||||
else: return False
|
||||
|
||||
elif len(proof) == PacketReceipt.IMPL_LENGTH:
|
||||
# This is an implicit proof
|
||||
|
||||
if not hasattr(self.destination, "identity"):
|
||||
return False
|
||||
|
||||
if self.destination.identity == None:
|
||||
return False
|
||||
if not hasattr(self.destination, "identity"): return False
|
||||
if self.destination.identity == None: return False
|
||||
|
||||
signature = proof[:RNS.Identity.SIGLENGTH//8]
|
||||
proof_valid = self.destination.identity.validate(signature, self.hash)
|
||||
@@ -537,16 +515,13 @@ class PacketReceipt:
|
||||
self.proof_packet = proof_packet
|
||||
|
||||
if self.callbacks.delivery != None:
|
||||
try:
|
||||
self.callbacks.delivery(self)
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
try: self.callbacks.delivery(self)
|
||||
except Exception as e: RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
else: return False
|
||||
else: return False
|
||||
|
||||
def get_rtt(self):
|
||||
"""
|
||||
@@ -559,10 +534,8 @@ class PacketReceipt:
|
||||
|
||||
def check_timeout(self):
|
||||
if self.status == PacketReceipt.SENT and self.is_timed_out():
|
||||
if self.timeout == -1:
|
||||
self.status = PacketReceipt.CULLED
|
||||
else:
|
||||
self.status = PacketReceipt.FAILED
|
||||
if self.timeout == -1: self.status = PacketReceipt.CULLED
|
||||
else: self.status = PacketReceipt.FAILED
|
||||
|
||||
self.concluded_at = time.time()
|
||||
|
||||
|
||||
+1
-2
@@ -31,5 +31,4 @@
|
||||
class Resolver:
|
||||
|
||||
@staticmethod
|
||||
def resolve_identity(full_name):
|
||||
pass
|
||||
def resolve_identity(full_name): pass
|
||||
+225
-251
@@ -48,6 +48,7 @@ else:
|
||||
|
||||
from RNS.vendor.configobj import ConfigObj
|
||||
from threading import Lock
|
||||
import RNS.vendor.umsgpack as mp
|
||||
import configparser
|
||||
import multiprocessing.connection
|
||||
import importlib.util
|
||||
@@ -251,6 +252,8 @@ class Reticulum:
|
||||
|
||||
Reticulum.__network_identity = None
|
||||
Reticulum.__transport_enabled = False
|
||||
Reticulum.__static_transport_identity = False
|
||||
Reticulum.__local_hops_delta = False
|
||||
Reticulum.__link_mtu_discovery = Reticulum.LINK_MTU_DISCOVERY
|
||||
Reticulum.__remote_management_enabled = False
|
||||
Reticulum.__use_implicit_proof = True
|
||||
@@ -346,7 +349,7 @@ class Reticulum:
|
||||
self.rpc_type = "AF_INET"
|
||||
|
||||
if self.rpc_key == None:
|
||||
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
||||
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.internal_identity().get_private_key())
|
||||
|
||||
if self.is_shared_instance:
|
||||
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, family=self.rpc_type, authkey=self.rpc_key)
|
||||
@@ -385,11 +388,7 @@ class Reticulum:
|
||||
def __start_local_interface(self):
|
||||
if self.share_instance:
|
||||
try:
|
||||
interface = LocalInterface.LocalServerInterface(
|
||||
RNS.Transport,
|
||||
self.local_interface_port,
|
||||
socket_path=self.local_socket_path
|
||||
)
|
||||
interface = LocalInterface.LocalServerInterface(RNS.Transport, self.local_interface_port, socket_path=self.local_socket_path)
|
||||
interface.OUT = True
|
||||
if hasattr(Reticulum, "_force_shared_instance_bitrate"):
|
||||
interface.bitrate = Reticulum._force_shared_instance_bitrate
|
||||
@@ -431,6 +430,7 @@ class Reticulum:
|
||||
Reticulum.__remote_management_enabled = False
|
||||
Reticulum.__allow_probes = False
|
||||
RNS.log("Connected to locally available Reticulum instance via: "+str(interface), RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
@@ -499,6 +499,14 @@ class Reticulum:
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__transport_enabled = True
|
||||
|
||||
if option == "static_transport_identity":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__static_transport_identity = True
|
||||
|
||||
if option == "local_hops_delta":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__local_hops_delta = True
|
||||
|
||||
if option == "network_identity":
|
||||
if Reticulum.__network_identity == None:
|
||||
path = self.config["reticulum"][option]
|
||||
@@ -668,6 +676,9 @@ class Reticulum:
|
||||
self.shared_instance_type = "tcp"
|
||||
self.use_af_unix = False
|
||||
|
||||
if self.shared_instance_type == "tcp":
|
||||
self.local_socket_path = None
|
||||
|
||||
if self.local_socket_path == None and self.use_af_unix:
|
||||
self.local_socket_path = "default"
|
||||
|
||||
@@ -706,6 +717,8 @@ class Reticulum:
|
||||
interface_mode = Interface.Interface.MODE_BOUNDARY
|
||||
elif c["mode"] == "gateway" or c["mode"] == "gw":
|
||||
interface_mode = Interface.Interface.MODE_GATEWAY
|
||||
elif c["mode"] == "internal":
|
||||
interface_mode = Interface.Interface.MODE_INTERNAL
|
||||
|
||||
elif "mode" in c:
|
||||
c["mode"] = str(c["mode"]).lower()
|
||||
@@ -721,27 +734,24 @@ class Reticulum:
|
||||
interface_mode = Interface.Interface.MODE_BOUNDARY
|
||||
elif c["mode"] == "gateway" or c["mode"] == "gw":
|
||||
interface_mode = Interface.Interface.MODE_GATEWAY
|
||||
elif c["mode"] == "internal":
|
||||
interface_mode = Interface.Interface.MODE_INTERNAL
|
||||
|
||||
ifac_size = None
|
||||
if "ifac_size" in c:
|
||||
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
|
||||
ifac_size = c.as_int("ifac_size")//8
|
||||
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8: ifac_size = c.as_int("ifac_size")//8
|
||||
|
||||
ifac_netname = None
|
||||
if "networkname" in c:
|
||||
if c["networkname"] != "":
|
||||
ifac_netname = c["networkname"]
|
||||
if c["networkname"] != "": ifac_netname = c["networkname"]
|
||||
if "network_name" in c:
|
||||
if c["network_name"] != "":
|
||||
ifac_netname = c["network_name"]
|
||||
if c["network_name"] != "": ifac_netname = c["network_name"]
|
||||
|
||||
ifac_netkey = None
|
||||
if "passphrase" in c:
|
||||
if c["passphrase"] != "":
|
||||
ifac_netkey = c["passphrase"]
|
||||
if c["passphrase"] != "": ifac_netkey = c["passphrase"]
|
||||
if "pass_phrase" in c:
|
||||
if c["pass_phrase"] != "":
|
||||
ifac_netkey = c["pass_phrase"]
|
||||
if c["pass_phrase"] != "": ifac_netkey = c["pass_phrase"]
|
||||
|
||||
ingress_control = True
|
||||
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
|
||||
@@ -770,29 +780,22 @@ class Reticulum:
|
||||
|
||||
configured_bitrate = None
|
||||
if "bitrate" in c:
|
||||
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
|
||||
configured_bitrate = c.as_int("bitrate")
|
||||
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE: configured_bitrate = c.as_int("bitrate")
|
||||
|
||||
announce_rate_target = None
|
||||
if "announce_rate_target" in c:
|
||||
if c.as_int("announce_rate_target") > 0:
|
||||
announce_rate_target = c.as_int("announce_rate_target")
|
||||
if c.as_int("announce_rate_target") > 0: announce_rate_target = c.as_int("announce_rate_target")
|
||||
|
||||
announce_rate_grace = None
|
||||
if "announce_rate_grace" in c:
|
||||
if c.as_int("announce_rate_grace") >= 0:
|
||||
announce_rate_grace = c.as_int("announce_rate_grace")
|
||||
if c.as_int("announce_rate_grace") >= 0: announce_rate_grace = c.as_int("announce_rate_grace")
|
||||
|
||||
announce_rate_penalty = None
|
||||
if "announce_rate_penalty" in c:
|
||||
if c.as_int("announce_rate_penalty") >= 0:
|
||||
announce_rate_penalty = c.as_int("announce_rate_penalty")
|
||||
if c.as_int("announce_rate_penalty") >= 0: announce_rate_penalty = c.as_int("announce_rate_penalty")
|
||||
|
||||
if announce_rate_target != None and announce_rate_grace == None:
|
||||
announce_rate_grace = 0
|
||||
|
||||
if announce_rate_target != None and announce_rate_penalty == None:
|
||||
announce_rate_penalty = 0
|
||||
if announce_rate_target != None and announce_rate_grace == None: announce_rate_grace = 0
|
||||
if announce_rate_target != None and announce_rate_penalty == None: announce_rate_penalty = 0
|
||||
|
||||
announce_cap = Reticulum.ANNOUNCE_CAP/100.0
|
||||
if "announce_cap" in c:
|
||||
@@ -802,6 +805,12 @@ class Reticulum:
|
||||
bootstrap_only = False
|
||||
if "bootstrap_only" in c: bootstrap_only = c.as_bool("bootstrap_only")
|
||||
|
||||
recursive_prs = False
|
||||
if "recursive_prs" in c: recursive_prs = c.as_bool("recursive_prs")
|
||||
|
||||
announces_from_internal = True
|
||||
if "announces_from_internal" in c: announces_from_internal = c.as_bool("announces_from_internal")
|
||||
|
||||
ignore_config_warnings = False
|
||||
if "ignore_config_warnings" in c: ignore_config_warnings = c.as_bool("ignore_config_warnings")
|
||||
|
||||
@@ -868,38 +877,41 @@ class Reticulum:
|
||||
if ifac_size != None: interface.ifac_size = ifac_size
|
||||
else: interface.ifac_size = interface.DEFAULT_IFAC_SIZE
|
||||
|
||||
interface.discoverable = discoverable
|
||||
interface.discovery_announce_interval = discovery_announce_interval
|
||||
interface.discovery_publish_ifac = publish_ifac
|
||||
interface.reachable_on = reachable_on
|
||||
interface.discovery_name = discovery_name
|
||||
interface.discovery_encrypt = discovery_encrypt
|
||||
interface.discovery_stamp_value = discovery_stamp_value
|
||||
interface.discovery_latitude = latitude
|
||||
interface.discovery_longitude = longitude
|
||||
interface.discovery_height = height
|
||||
interface.discovery_frequency = discovery_frequency
|
||||
interface.discovery_bandwidth = discovery_bandwidth
|
||||
interface.discovery_modulation = discovery_modulation
|
||||
interface.discoverable = discoverable
|
||||
interface.discovery_announce_interval = discovery_announce_interval
|
||||
interface.discovery_publish_ifac = publish_ifac
|
||||
interface.reachable_on = reachable_on
|
||||
interface.discovery_name = discovery_name
|
||||
interface.discovery_encrypt = discovery_encrypt
|
||||
interface.discovery_stamp_value = discovery_stamp_value
|
||||
interface.discovery_latitude = latitude
|
||||
interface.discovery_longitude = longitude
|
||||
interface.discovery_height = height
|
||||
interface.discovery_frequency = discovery_frequency
|
||||
interface.discovery_bandwidth = discovery_bandwidth
|
||||
interface.discovery_modulation = discovery_modulation
|
||||
|
||||
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 egress_control != None: interface.egress_control = egress_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_pr_burst_freq_new != None: interface.ic_pr_burst_freq_new = ic_pr_burst_freq_new
|
||||
if ic_pr_burst_freq != None: interface.ic_pr_burst_freq = ic_pr_burst_freq
|
||||
if ec_pr_freq != None: interface.ec_pr_freq = ec_pr_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
|
||||
interface.recursive_prs = recursive_prs
|
||||
interface.announces_from_internal = announces_from_internal
|
||||
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 egress_control != None: interface.egress_control = egress_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_pr_burst_freq_new != None: interface.ic_pr_burst_freq_new = ic_pr_burst_freq_new
|
||||
if ic_pr_burst_freq != None: interface.ic_pr_burst_freq = ic_pr_burst_freq
|
||||
if ec_pr_freq != None: interface.ec_pr_freq = ec_pr_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
|
||||
interface.ifac_netkey = ifac_netkey
|
||||
|
||||
if interface.ifac_netname != None or interface.ifac_netkey != None:
|
||||
ifac_origin = b""
|
||||
@@ -911,12 +923,8 @@ class Reticulum:
|
||||
ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
|
||||
|
||||
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
|
||||
interface.ifac_key = RNS.Cryptography.hkdf(
|
||||
length=64,
|
||||
derive_from=ifac_origin_hash,
|
||||
salt=self.ifac_salt,
|
||||
context=None
|
||||
)
|
||||
interface.ifac_key = RNS.Cryptography.hkdf(length=64, derive_from=ifac_origin_hash,
|
||||
salt=self.ifac_salt, context=None)
|
||||
|
||||
interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
|
||||
interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
|
||||
@@ -1040,7 +1048,9 @@ class Reticulum:
|
||||
RNS.panic()
|
||||
|
||||
def _add_interface(self, interface, mode = None, configured_bitrate=None, ifac_size=None, ifac_netname=None, ifac_netkey=None,
|
||||
announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None, bootstrap_only=False):
|
||||
announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None,
|
||||
bootstrap_only=False, recursive_prs=False, announces_from_internal=True):
|
||||
|
||||
if not self.is_connected_to_shared_instance:
|
||||
if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface):
|
||||
|
||||
@@ -1055,13 +1065,15 @@ class Reticulum:
|
||||
if ifac_size != None: interface.ifac_size = ifac_size
|
||||
else: interface.ifac_size = interface.DEFAULT_IFAC_SIZE
|
||||
|
||||
interface.announce_cap = announce_cap if announce_cap != None else Reticulum.ANNOUNCE_CAP/100.0
|
||||
interface.announce_rate_target = announce_rate_target
|
||||
interface.announce_rate_grace = announce_rate_grace
|
||||
interface.announce_rate_penalty = announce_rate_penalty
|
||||
interface.recursive_prs = recursive_prs
|
||||
interface.announces_from_internal = announces_from_internal
|
||||
interface.announce_cap = announce_cap if announce_cap != None else Reticulum.ANNOUNCE_CAP/100.0
|
||||
interface.announce_rate_target = announce_rate_target
|
||||
interface.announce_rate_grace = announce_rate_grace
|
||||
interface.announce_rate_penalty = announce_rate_penalty
|
||||
|
||||
interface.ifac_netname = ifac_netname
|
||||
interface.ifac_netkey = ifac_netkey
|
||||
interface.ifac_netkey = ifac_netkey
|
||||
|
||||
if interface.ifac_netname != None or interface.ifac_netkey != None:
|
||||
ifac_origin = b""
|
||||
@@ -1073,12 +1085,8 @@ class Reticulum:
|
||||
ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
|
||||
|
||||
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
|
||||
interface.ifac_key = RNS.Cryptography.hkdf(
|
||||
length=64,
|
||||
derive_from=ifac_origin_hash,
|
||||
salt=self.ifac_salt,
|
||||
context=None
|
||||
)
|
||||
interface.ifac_key = RNS.Cryptography.hkdf(length=64, derive_from=ifac_origin_hash,
|
||||
salt=self.ifac_salt, context=None)
|
||||
|
||||
interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
|
||||
interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
|
||||
@@ -1175,64 +1183,66 @@ class Reticulum:
|
||||
self.config = ConfigObj(__default_rns_config__)
|
||||
self.config.filename = Reticulum.configpath
|
||||
|
||||
if not os.path.isdir(Reticulum.configdir):
|
||||
os.makedirs(Reticulum.configdir)
|
||||
if not os.path.isdir(Reticulum.configdir): os.makedirs(Reticulum.configdir)
|
||||
self.config.write()
|
||||
|
||||
def rpc_return(self, connection, response):
|
||||
connection.send_bytes(mp.packb(response))
|
||||
|
||||
def rpc_loop(self):
|
||||
while RNS.Transport._should_run:
|
||||
try:
|
||||
rpc_connection = self.rpc_listener.accept()
|
||||
call = rpc_connection.recv()
|
||||
conn = self.rpc_listener.accept()
|
||||
call = mp.unpackb(conn.recv_bytes())
|
||||
|
||||
if "get" in call:
|
||||
path = call["get"]
|
||||
|
||||
if path == "path_table":
|
||||
mh = call["max_hops"]
|
||||
rpc_connection.send(self.get_path_table(max_hops=mh))
|
||||
self.rpc_return(conn, self.get_path_table(max_hops=mh))
|
||||
|
||||
if path == "interface_stats": rpc_connection.send(self.get_interface_stats())
|
||||
if path == "rate_table": rpc_connection.send(self.get_rate_table())
|
||||
if path == "next_hop_if_name": rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"]))
|
||||
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 == "link_count": rpc_connection.send(self.get_link_count())
|
||||
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 path == "blackholed_identities": rpc_connection.send(self.get_blackholed_identities())
|
||||
if path == "is_blackholed": rpc_connection.send(self.is_blackholed(call["identity_hash"]))
|
||||
if path == "interface_stats": self.rpc_return(conn, self.get_interface_stats())
|
||||
if path == "rate_table": self.rpc_return(conn, self.get_rate_table())
|
||||
if path == "next_hop_if_name": self.rpc_return(conn, self.get_next_hop_if_name(call["destination_hash"]))
|
||||
if path == "next_hop": self.rpc_return(conn, self.get_next_hop(call["destination_hash"]))
|
||||
if path == "first_hop_timeout": self.rpc_return(conn, self.get_first_hop_timeout(call["destination_hash"]))
|
||||
if path == "link_count": self.rpc_return(conn, self.get_link_count())
|
||||
if path == "packet_rssi": self.rpc_return(conn, self.get_packet_rssi(call["packet_hash"]))
|
||||
if path == "packet_snr": self.rpc_return(conn, self.get_packet_snr(call["packet_hash"]))
|
||||
if path == "packet_q": self.rpc_return(conn, self.get_packet_q(call["packet_hash"]))
|
||||
if path == "blackholed_identities": self.rpc_return(conn, self.get_blackholed_identities())
|
||||
if path == "is_blackholed": self.rpc_return(conn, self.is_blackholed(call["identity_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())
|
||||
if path == "path": self.rpc_return(conn, self.drop_path(call["destination_hash"]))
|
||||
if path == "all_via": self.rpc_return(conn, self.drop_all_via(call["destination_hash"]))
|
||||
if path == "announce_queues": self.rpc_return(conn, self.drop_announce_queues())
|
||||
|
||||
if "blackhole_identity" in call:
|
||||
identity_hash = call["blackhole_identity"]
|
||||
until = call["until"]
|
||||
reason = call["reason"]
|
||||
rpc_connection.send(self.blackhole_identity(identity_hash, until=until, reason=reason))
|
||||
self.rpc_return(conn, self.blackhole_identity(identity_hash, until=until, reason=reason))
|
||||
|
||||
if "unblackhole_identity" in call:
|
||||
identity_hash = call["unblackhole_identity"]
|
||||
rpc_connection.send(self.unblackhole_identity(identity_hash))
|
||||
self.rpc_return(conn, self.unblackhole_identity(identity_hash))
|
||||
|
||||
if "destination_data" in call:
|
||||
operation = call["destination_data"]
|
||||
destination_hash = call["destination_hash"]
|
||||
if operation == "used": rpc_connection.send(self._used_destination_data(destination_hash))
|
||||
elif operation == "retain": rpc_connection.send(self._retain_destination_data(destination_hash))
|
||||
elif operation == "unretain": rpc_connection.send(self._unretain_destination_data(destination_hash))
|
||||
if operation == "used": self.rpc_return(conn, self._used_destination_data(destination_hash))
|
||||
elif operation == "retain": self.rpc_return(conn, self._retain_destination_data(destination_hash))
|
||||
elif operation == "unretain": self.rpc_return(conn, self._unretain_destination_data(destination_hash))
|
||||
|
||||
if "identity_data" in call:
|
||||
operation = call["identity_data"]
|
||||
identity_hash = call["identity_hash"]
|
||||
if operation == "retain": rpc_connection.send(self._retain_identity(identity_hash))
|
||||
if operation == "retain": self.rpc_return(conn, self._retain_identity(identity_hash))
|
||||
|
||||
rpc_connection.close()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error ocurred while handling RPC call from local client: "+str(e), RNS.LOG_ERROR)
|
||||
@@ -1243,8 +1253,8 @@ class Reticulum:
|
||||
if self.is_connected_to_shared_instance:
|
||||
try:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"destination_data": "used", "destination_hash": destination_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"destination_data": "used", "destination_hash": destination_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
@@ -1257,8 +1267,8 @@ class Reticulum:
|
||||
if self.is_connected_to_shared_instance:
|
||||
try:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"destination_data": "retain", "destination_hash": destination_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"destination_data": "retain", "destination_hash": destination_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
@@ -1271,8 +1281,8 @@ class Reticulum:
|
||||
if self.is_connected_to_shared_instance:
|
||||
try:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"destination_data": "unretain", "destination_hash": destination_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"destination_data": "unretain", "destination_hash": destination_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
@@ -1288,8 +1298,8 @@ class Reticulum:
|
||||
if self.is_connected_to_shared_instance:
|
||||
try:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"identity_data": "retain", "identity_hash": identity_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"identity_data": "retain", "identity_hash": identity_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
@@ -1301,34 +1311,29 @@ class Reticulum:
|
||||
def get_interface_stats(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "interface_stats"})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "interface_stats"}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
interfaces = []
|
||||
for interface in RNS.Transport.interfaces:
|
||||
ifstats = {}
|
||||
|
||||
if hasattr(interface, "clients"):
|
||||
ifstats["clients"] = interface.clients
|
||||
else:
|
||||
ifstats["clients"] = None
|
||||
if hasattr(interface, "clients"): ifstats["clients"] = interface.clients
|
||||
else: ifstats["clients"] = None
|
||||
|
||||
if hasattr(interface, "parent_interface") and interface.parent_interface != None:
|
||||
ifstats["parent_interface_name"] = str(interface.parent_interface)
|
||||
ifstats["parent_interface_hash"] = interface.parent_interface.get_hash()
|
||||
|
||||
if hasattr(interface, "i2p") and hasattr(interface, "connectable"):
|
||||
if interface.connectable:
|
||||
ifstats["i2p_connectable"] = True
|
||||
else:
|
||||
ifstats["i2p_connectable"] = False
|
||||
if interface.connectable: ifstats["i2p_connectable"] = True
|
||||
else: ifstats["i2p_connectable"] = False
|
||||
|
||||
if hasattr(interface, "b32"):
|
||||
if interface.b32 != None:
|
||||
ifstats["i2p_b32"] = interface.b32+".b32.i2p"
|
||||
else:
|
||||
ifstats["i2p_b32"] = None
|
||||
if interface.b32 != None: ifstats["i2p_b32"] = interface.b32+".b32.i2p"
|
||||
else: ifstats["i2p_b32"] = None
|
||||
|
||||
if hasattr(interface, "i2p_tunnel_state"):
|
||||
if interface.i2p_tunnel_state != None:
|
||||
@@ -1343,37 +1348,20 @@ class Reticulum:
|
||||
else:
|
||||
ifstats["tunnelstate"] = None
|
||||
|
||||
if hasattr(interface, "r_airtime_short"):
|
||||
ifstats["airtime_short"] = interface.r_airtime_short
|
||||
|
||||
if hasattr(interface, "r_airtime_long"):
|
||||
ifstats["airtime_long"] = interface.r_airtime_long
|
||||
|
||||
if hasattr(interface, "r_channel_load_short"):
|
||||
ifstats["channel_load_short"] = interface.r_channel_load_short
|
||||
|
||||
if hasattr(interface, "r_channel_load_long"):
|
||||
ifstats["channel_load_long"] = interface.r_channel_load_long
|
||||
|
||||
if hasattr(interface, "r_noise_floor"):
|
||||
ifstats["noise_floor"] = interface.r_noise_floor
|
||||
|
||||
if hasattr(interface, "r_interference"):
|
||||
ifstats["interference"] = interface.r_interference
|
||||
if hasattr(interface, "r_airtime_short"): ifstats["airtime_short"] = interface.r_airtime_short
|
||||
if hasattr(interface, "r_airtime_long"): ifstats["airtime_long"] = interface.r_airtime_long
|
||||
if hasattr(interface, "r_channel_load_short"): ifstats["channel_load_short"] = interface.r_channel_load_short
|
||||
if hasattr(interface, "r_channel_load_long"): ifstats["channel_load_long"] = interface.r_channel_load_long
|
||||
if hasattr(interface, "r_noise_floor"): ifstats["noise_floor"] = interface.r_noise_floor
|
||||
if hasattr(interface, "r_interference"): ifstats["interference"] = interface.r_interference
|
||||
if hasattr(interface, "cpu_temp"): ifstats["cpu_temp"] = interface.cpu_temp
|
||||
if hasattr(interface, "cpu_load"): ifstats["cpu_load"] = interface.cpu_load
|
||||
if hasattr(interface, "mem_load"): ifstats["mem_load"] = interface.mem_load
|
||||
|
||||
if hasattr(interface, "r_interference_l") and type(interface.r_interference_l) == list:
|
||||
ifstats["interference_last_ts"] = interface.r_interference_l[0]
|
||||
ifstats["interference_last_dbm"] = interface.r_interference_l[1]
|
||||
|
||||
if hasattr(interface, "cpu_temp"):
|
||||
ifstats["cpu_temp"] = interface.cpu_temp
|
||||
|
||||
if hasattr(interface, "cpu_load"):
|
||||
ifstats["cpu_load"] = interface.cpu_load
|
||||
|
||||
if hasattr(interface, "mem_load"):
|
||||
ifstats["mem_load"] = interface.mem_load
|
||||
|
||||
if hasattr(interface, "switch_id"):
|
||||
if interface.switch_id != None: ifstats["switch_id"] = RNS.hexrep(interface.switch_id)
|
||||
else: ifstats["switch_id"] = None
|
||||
@@ -1387,39 +1375,26 @@ class Reticulum:
|
||||
else: ifstats["endpoint_id"] = None
|
||||
|
||||
if hasattr(interface, "r_battery_state"):
|
||||
if interface.r_battery_state != 0x00:
|
||||
ifstats["battery_state"] = interface.get_battery_state_string()
|
||||
|
||||
if hasattr(interface, "r_battery_percent"):
|
||||
ifstats["battery_percent"] = interface.r_battery_percent
|
||||
if interface.r_battery_state != 0x00: ifstats["battery_state"] = interface.get_battery_state_string()
|
||||
if hasattr(interface, "r_battery_percent"): ifstats["battery_percent"] = interface.r_battery_percent
|
||||
|
||||
if hasattr(interface, "bitrate"):
|
||||
if interface.bitrate != None:
|
||||
ifstats["bitrate"] = interface.bitrate
|
||||
else:
|
||||
ifstats["bitrate"] = None
|
||||
if interface.bitrate != None: ifstats["bitrate"] = interface.bitrate
|
||||
else: ifstats["bitrate"] = None
|
||||
|
||||
if hasattr(interface, "current_rx_speed"):
|
||||
if interface.current_rx_speed != None:
|
||||
ifstats["rxs"] = interface.current_rx_speed
|
||||
else:
|
||||
ifstats["rxs"] = 0
|
||||
else:
|
||||
ifstats["rxs"] = 0
|
||||
if interface.current_rx_speed != None: ifstats["rxs"] = interface.current_rx_speed
|
||||
else: ifstats["rxs"] = 0
|
||||
else: ifstats["rxs"] = 0
|
||||
|
||||
if hasattr(interface, "current_tx_speed"):
|
||||
if interface.current_tx_speed != None:
|
||||
ifstats["txs"] = interface.current_tx_speed
|
||||
else:
|
||||
ifstats["txs"] = 0
|
||||
else:
|
||||
ifstats["txs"] = 0
|
||||
if interface.current_tx_speed != None: ifstats["txs"] = interface.current_tx_speed
|
||||
else: ifstats["txs"] = 0
|
||||
else: ifstats["txs"] = 0
|
||||
|
||||
if hasattr(interface, "peers"):
|
||||
if interface.peers != None:
|
||||
ifstats["peers"] = len(interface.peers)
|
||||
else:
|
||||
ifstats["peers"] = None
|
||||
if interface.peers != None: ifstats["peers"] = len(interface.peers)
|
||||
else: ifstats["peers"] = None
|
||||
|
||||
if hasattr(interface, "ifac_signature"):
|
||||
ifstats["ifac_signature"] = interface.ifac_signature
|
||||
@@ -1427,57 +1402,52 @@ class Reticulum:
|
||||
ifstats["ifac_netname"] = interface.ifac_netname
|
||||
else:
|
||||
ifstats["ifac_signature"] = None
|
||||
ifstats["ifac_size"] = None
|
||||
ifstats["ifac_netname"] = None
|
||||
ifstats["ifac_size"] = None
|
||||
ifstats["ifac_netname"] = None
|
||||
|
||||
if hasattr(interface, "autoconnect_source"):
|
||||
ifstats["autoconnect_source"] = interface.autoconnect_source
|
||||
else:
|
||||
ifstats["autoconnect_source"] = None
|
||||
if hasattr(interface, "autoconnect_source"): ifstats["autoconnect_source"] = interface.autoconnect_source
|
||||
else: ifstats["autoconnect_source"] = None
|
||||
|
||||
if hasattr(interface, "announce_queue"):
|
||||
if interface.announce_queue != None:
|
||||
ifstats["announce_queue"] = len(interface.announce_queue)
|
||||
else:
|
||||
ifstats["announce_queue"] = None
|
||||
if interface.announce_queue != None: ifstats["announce_queue"] = len(interface.announce_queue)
|
||||
else: ifstats["announce_queue"] = None
|
||||
|
||||
ifstats["name"] = str(interface)
|
||||
ifstats["short_name"] = str(interface.name)
|
||||
ifstats["hash"] = interface.get_hash()
|
||||
ifstats["type"] = str(type(interface).__name__)
|
||||
ifstats["rxb"] = interface.rxb
|
||||
ifstats["txb"] = interface.txb
|
||||
ifstats["name"] = str(interface)
|
||||
ifstats["short_name"] = str(interface.name)
|
||||
ifstats["hash"] = interface.get_hash()
|
||||
ifstats["type"] = str(type(interface).__name__)
|
||||
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["incoming_pr_frequency"] = interface.incoming_pr_frequency()
|
||||
ifstats["outgoing_pr_frequency"] = interface.outgoing_pr_frequency()
|
||||
ifstats["announce_rate_target"] = interface.announce_rate_target
|
||||
ifstats["announce_rate_penalty"] = interface.announce_rate_penalty
|
||||
ifstats["announce_rate_grace"] = interface.announce_rate_grace
|
||||
ifstats["held_announces"] = len(interface.held_announces)
|
||||
ifstats["burst_active"] = interface.ic_burst_active
|
||||
ifstats["burst_activated"] = interface.ic_burst_activated
|
||||
ifstats["pr_burst_active"] = interface.ic_pr_burst_active
|
||||
ifstats["pr_burst_activated"] = interface.ic_pr_burst_activated
|
||||
ifstats["status"] = interface.online
|
||||
ifstats["mode"] = interface.mode
|
||||
ifstats["incoming_pr_frequency"] = interface.incoming_pr_frequency()
|
||||
ifstats["outgoing_pr_frequency"] = interface.outgoing_pr_frequency()
|
||||
ifstats["announce_rate_target"] = interface.announce_rate_target
|
||||
ifstats["announce_rate_penalty"] = interface.announce_rate_penalty
|
||||
ifstats["announce_rate_grace"] = interface.announce_rate_grace
|
||||
ifstats["held_announces"] = len(interface.held_announces)
|
||||
ifstats["burst_active"] = interface.ic_burst_active
|
||||
ifstats["burst_activated"] = interface.ic_burst_activated
|
||||
ifstats["pr_burst_active"] = interface.ic_pr_burst_active
|
||||
ifstats["pr_burst_activated"] = interface.ic_pr_burst_activated
|
||||
ifstats["status"] = interface.online
|
||||
ifstats["mode"] = interface.mode
|
||||
|
||||
interfaces.append(ifstats)
|
||||
|
||||
stats = {}
|
||||
stats = {}
|
||||
stats["interfaces"] = interfaces
|
||||
stats["rxb"] = RNS.Transport.traffic_rxb
|
||||
stats["txb"] = RNS.Transport.traffic_txb
|
||||
stats["rxs"] = RNS.Transport.speed_rx
|
||||
stats["txs"] = RNS.Transport.speed_tx
|
||||
stats["rxb"] = RNS.Transport.traffic_rxb
|
||||
stats["txb"] = RNS.Transport.traffic_txb
|
||||
stats["rxs"] = RNS.Transport.speed_rx
|
||||
stats["txs"] = RNS.Transport.speed_tx
|
||||
|
||||
if Reticulum.transport_enabled():
|
||||
stats["transport_id"] = RNS.Transport.identity.hash
|
||||
stats["network_id"] = RNS.Transport.network_identity.hash if RNS.Transport.network_identity else None
|
||||
stats["transport_uptime"] = time.time()-RNS.Transport.start_time
|
||||
if Reticulum.probe_destination_enabled():
|
||||
stats["probe_responder"] = RNS.Transport.probe_destination.hash
|
||||
else:
|
||||
stats["probe_responder"] = None
|
||||
if Reticulum.probe_destination_enabled(): stats["probe_responder"] = RNS.Transport.probe_destination.hash
|
||||
else: stats["probe_responder"] = None
|
||||
|
||||
if importlib.util.find_spec('psutil') != None:
|
||||
import psutil
|
||||
@@ -1491,8 +1461,8 @@ class Reticulum:
|
||||
def get_path_table(self, max_hops=None):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "path_table", "max_hops": max_hops})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "path_table", "max_hops": max_hops}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1500,14 +1470,10 @@ class Reticulum:
|
||||
for dst_hash in RNS.Transport.path_table:
|
||||
path_hops = RNS.Transport.path_table[dst_hash][2]
|
||||
if max_hops == None or path_hops <= max_hops:
|
||||
entry = {
|
||||
"hash": dst_hash,
|
||||
"timestamp": RNS.Transport.path_table[dst_hash][0],
|
||||
"via": RNS.Transport.path_table[dst_hash][1],
|
||||
"hops": path_hops,
|
||||
"expires": RNS.Transport.path_table[dst_hash][3],
|
||||
"interface": str(RNS.Transport.path_table[dst_hash][5]),
|
||||
}
|
||||
entry = { "hash": dst_hash, "timestamp": RNS.Transport.path_table[dst_hash][0],
|
||||
"via": RNS.Transport.path_table[dst_hash][1], "hops": path_hops,
|
||||
"expires": RNS.Transport.path_table[dst_hash][3],
|
||||
"interface": str(RNS.Transport.path_table[dst_hash][5]) }
|
||||
path_table.append(entry)
|
||||
|
||||
return path_table
|
||||
@@ -1515,8 +1481,8 @@ class Reticulum:
|
||||
def get_rate_table(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "rate_table"})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "rate_table"}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1536,8 +1502,8 @@ class Reticulum:
|
||||
def drop_path(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"drop": "path", "destination_hash": destination})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"drop": "path", "destination_hash": destination}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1546,8 +1512,8 @@ class Reticulum:
|
||||
def drop_all_via(self, transport_hash):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"drop": "all_via", "destination_hash": transport_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"drop": "all_via", "destination_hash": transport_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1562,8 +1528,8 @@ class Reticulum:
|
||||
def drop_announce_queues(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"drop": "announce_queues"})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"drop": "announce_queues"}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1572,8 +1538,8 @@ class Reticulum:
|
||||
def get_next_hop_if_name(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "next_hop_if_name", "destination_hash": destination})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "next_hop_if_name", "destination_hash": destination}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1583,8 +1549,8 @@ class Reticulum:
|
||||
if self.is_connected_to_shared_instance:
|
||||
try:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "first_hop_timeout", "destination_hash": destination})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "first_hop_timeout", "destination_hash": destination}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
|
||||
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
|
||||
@@ -1602,8 +1568,8 @@ class Reticulum:
|
||||
def get_next_hop(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "next_hop", "destination_hash": destination})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "next_hop", "destination_hash": destination}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
|
||||
return response
|
||||
|
||||
@@ -1613,8 +1579,8 @@ class Reticulum:
|
||||
def get_link_count(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "link_count"})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "link_count"}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1623,8 +1589,8 @@ class Reticulum:
|
||||
def get_packet_rssi(self, packet_hash):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "packet_rssi", "packet_hash": packet_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "packet_rssi", "packet_hash": packet_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1637,8 +1603,8 @@ class Reticulum:
|
||||
def get_packet_snr(self, packet_hash):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "packet_snr", "packet_hash": packet_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "packet_snr", "packet_hash": packet_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1651,8 +1617,8 @@ class Reticulum:
|
||||
def get_packet_q(self, packet_hash):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "packet_q", "packet_hash": packet_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "packet_q", "packet_hash": packet_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else:
|
||||
@@ -1674,8 +1640,8 @@ class Reticulum:
|
||||
def get_blackholed_identities(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "blackholed_identities"})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "blackholed_identities"}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else: return RNS.Transport.blackholed_identities
|
||||
@@ -1688,8 +1654,8 @@ class Reticulum:
|
||||
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "is_blackholed", "identity_hash": identity_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"get": "is_blackholed", "identity_hash": identity_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else: return identity_hash in RNS.Transport.blackholed_identities
|
||||
@@ -1699,8 +1665,8 @@ class Reticulum:
|
||||
else:
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"blackhole_identity": identity_hash, "until": until, "reason": reason})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"blackhole_identity": identity_hash, "until": until, "reason": reason}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else: return RNS.Transport.blackhole_identity(identity_hash, until=until, reason=reason)
|
||||
@@ -1710,8 +1676,8 @@ class Reticulum:
|
||||
else:
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"unblackhole_identity": identity_hash})
|
||||
response = rpc_connection.recv()
|
||||
rpc_connection.send_bytes(mp.packb({"unblackhole_identity": identity_hash}))
|
||||
response = mp.unpackb(rpc_connection.recv_bytes())
|
||||
return response
|
||||
|
||||
else: return RNS.Transport.unblackhole_identity(identity_hash)
|
||||
@@ -1831,6 +1797,14 @@ class Reticulum:
|
||||
def max_autoconnected_interfaces():
|
||||
return Reticulum.__autoconnect_discovered_interfaces
|
||||
|
||||
@staticmethod
|
||||
def static_transport_identity():
|
||||
return Reticulum.__static_transport_identity
|
||||
|
||||
@staticmethod
|
||||
def local_hops_delta():
|
||||
return Reticulum.__local_hops_delta
|
||||
|
||||
# Default configuration file:
|
||||
__default_rns_config__ = '''# This is the default Reticulum config file.
|
||||
# You should probably edit it to include any additional,
|
||||
|
||||
+148
-120
@@ -166,6 +166,7 @@ class Transport:
|
||||
local_client_rssi_cache = []
|
||||
local_client_snr_cache = []
|
||||
local_client_q_cache = []
|
||||
local_hops_delta = 0
|
||||
LOCAL_CLIENT_CACHE_MAXSIZE = 512
|
||||
|
||||
pending_local_path_requests = {}
|
||||
@@ -207,8 +208,12 @@ class Transport:
|
||||
identity = None
|
||||
network_identity = None
|
||||
|
||||
_identity = None
|
||||
_should_run = True
|
||||
|
||||
@staticmethod
|
||||
def internal_identity(): return Transport._identity
|
||||
|
||||
@staticmethod
|
||||
def start(reticulum_instance):
|
||||
Transport.owner = reticulum_instance
|
||||
@@ -226,6 +231,14 @@ class Transport:
|
||||
else:
|
||||
RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE) if RNS.sl(RNS.LOG_VERBOSE) else None
|
||||
|
||||
Transport._identity = Transport.identity
|
||||
if not RNS.Reticulum.transport_enabled() and not RNS.Reticulum.static_transport_identity():
|
||||
Transport._identity = Transport.identity
|
||||
Transport.identity = RNS.Identity()
|
||||
RNS.log(f"Initialized ephemeral transport identity {RNS.prettyhexrep(Transport.identity.hash)}", RNS.LOG_VERBOSE) if RNS.sl(RNS.LOG_VERBOSE) else None
|
||||
|
||||
if RNS.Reticulum.local_hops_delta(): Transport.local_hops_delta = (ord(os.urandom(1))%6)+2
|
||||
|
||||
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
|
||||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
if os.path.isfile(packet_hashlist_path):
|
||||
@@ -318,7 +331,7 @@ class Transport:
|
||||
del path_identity
|
||||
|
||||
if announce_packet != None and receiving_interface != None and blackholed == False:
|
||||
announce_packet.unpack()
|
||||
if not announce_packet.unpack(): continue
|
||||
# We increase the hops, since reading a packet
|
||||
# from cache is equivalent to receiving it again
|
||||
# over an interface. It is cached with it's non-
|
||||
@@ -371,7 +384,7 @@ class Transport:
|
||||
announce_packet = Transport.get_cached_packet(serialised_entry[7], packet_type="announce")
|
||||
|
||||
if announce_packet != None:
|
||||
announce_packet.unpack()
|
||||
if not announce_packet.unpack(): continue
|
||||
# We increase the hops, since reading a packet
|
||||
# from cache is equivalent to receiving it again
|
||||
# over an interface. It is cached with it's non-
|
||||
@@ -621,7 +634,7 @@ class Transport:
|
||||
|
||||
outgoing.append(new_packet)
|
||||
|
||||
# This handles an edge case where a peer sends a past
|
||||
# This handles an edge case where a peer sends a path
|
||||
# request for a destination just after an announce for
|
||||
# said destination has arrived, but before it has been
|
||||
# rebroadcast locally. In such a case the actual announce
|
||||
@@ -1137,7 +1150,7 @@ class Transport:
|
||||
# Insert packet into transport
|
||||
new_flags = (RNS.Packet.HEADER_2) << 6 | (Transport.TRANSPORT) << 4 | (packet.flags & 0b00001111)
|
||||
new_raw = struct.pack("!B", new_flags)
|
||||
new_raw += packet.raw[1:2]
|
||||
new_raw += packet.raw[1:2] if not Transport.should_apply_delta(packet, outbound_interface) else struct.pack("!B", Transport.local_hops_delta)
|
||||
new_raw += path_entry[IDX_PT_NEXT_HOP]
|
||||
new_raw += packet.raw[2:]
|
||||
packet_sent(packet)
|
||||
@@ -1157,7 +1170,7 @@ class Transport:
|
||||
# Insert packet into transport
|
||||
new_flags = (RNS.Packet.HEADER_2) << 6 | (Transport.TRANSPORT) << 4 | (packet.flags & 0b00001111)
|
||||
new_raw = struct.pack("!B", new_flags)
|
||||
new_raw += packet.raw[1:2]
|
||||
new_raw += packet.raw[1:2] if not Transport.should_apply_delta(packet, outbound_interface) else struct.pack("!B", Transport.local_hops_delta)
|
||||
new_raw += path_entry[IDX_PT_NEXT_HOP]
|
||||
new_raw += packet.raw[2:]
|
||||
packet_sent(packet)
|
||||
@@ -1170,7 +1183,9 @@ class Transport:
|
||||
# simply transmit the packet directly on that one.
|
||||
else:
|
||||
packet_sent(packet)
|
||||
Transport.transmit(outbound_interface, packet.raw)
|
||||
if not Transport.should_apply_delta(packet, outbound_interface): raw = packet.raw
|
||||
else: raw = Transport.mangle_hops(packet.raw, Transport.local_hops_delta)
|
||||
Transport.transmit(outbound_interface, raw)
|
||||
sent = True
|
||||
|
||||
# If we don't have a known path for the destination, we'll
|
||||
@@ -1184,63 +1199,68 @@ class Transport:
|
||||
should_transmit = True
|
||||
|
||||
if packet.destination.type == RNS.Destination.LINK:
|
||||
if packet.destination.status == RNS.Link.CLOSED: should_transmit = False
|
||||
if interface != packet.destination.attached_interface: should_transmit = False
|
||||
|
||||
if packet.attached_interface != None and interface != packet.attached_interface:
|
||||
should_transmit = False
|
||||
if packet.destination.status == RNS.Link.CLOSED: continue
|
||||
if interface != packet.destination.attached_interface: continue
|
||||
|
||||
if packet.attached_interface != None and interface != packet.attached_interface: continue
|
||||
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||
if packet.attached_interface == None:
|
||||
if interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to AP mode", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
ac_loglevel = RNS.LOG_EXTREME
|
||||
from_interface = Transport.next_hop_interface(packet.destination_hash)
|
||||
local_destination = None
|
||||
with Transport.destinations_map_lock:
|
||||
if packet.destination_hash in Transport.destinations_map:
|
||||
local_destination = Transport.destinations_map[packet.destination_hash]
|
||||
|
||||
if not local_destination and from_interface == None:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface doesn't exist", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
|
||||
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
local_destination = None
|
||||
with Transport.destinations_map_lock:
|
||||
if packet.destination_hash in Transport.destinations_map:
|
||||
local_destination = Transport.destinations_map[packet.destination_hash]
|
||||
elif not local_destination and interface.announces_from_internal == False and from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_INTERNAL:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to internal-mode next hop interface", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
|
||||
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to AP mode", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
|
||||
elif not local_destination and interface.mode == RNS.Interfaces.Interface.Interface.MODE_INTERNAL:
|
||||
if not hasattr(from_interface, "mode"):
|
||||
should_transmit = False
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
else:
|
||||
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to boundary-mode next-hop interface", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
|
||||
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
if local_destination != None:
|
||||
# RNS.log("Allowing announce broadcast on roaming-mode interface from instance-local destination", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
# RNS.log("Allowing announce broadcast on roaming-mode interface from instance-local destination", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
pass
|
||||
else:
|
||||
from_interface = Transport.next_hop_interface(packet.destination_hash)
|
||||
if from_interface == None or not hasattr(from_interface, "mode"):
|
||||
if not hasattr(from_interface, "mode"):
|
||||
should_transmit = False
|
||||
if from_interface == None:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface doesn't exist", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
elif not hasattr(from_interface, "mode"):
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
else:
|
||||
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
elif from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to boundary-mode next-hop interface", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to boundary-mode next-hop interface", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
|
||||
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||
local_destination = None
|
||||
with Transport.destinations_map_lock:
|
||||
if packet.destination_hash in Transport.destinations_map:
|
||||
local_destination = Transport.destinations_map[packet.destination_hash]
|
||||
|
||||
if local_destination != None:
|
||||
# RNS.log("Allowing announce broadcast on boundary-mode interface from instance-local destination", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
# RNS.log("Allowing announce broadcast on boundary-mode interface from instance-local destination", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
pass
|
||||
else:
|
||||
from_interface = Transport.next_hop_interface(packet.destination_hash)
|
||||
if from_interface == None or not hasattr(from_interface, "mode"):
|
||||
if not hasattr(from_interface, "mode"):
|
||||
should_transmit = False
|
||||
if from_interface == None:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface doesn't exist", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
elif not hasattr(from_interface, "mode"):
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
else:
|
||||
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", ac_loglevel) if RNS.sl(ac_loglevel) else None
|
||||
should_transmit = False
|
||||
|
||||
else:
|
||||
@@ -1319,7 +1339,12 @@ class Transport:
|
||||
Transport.add_packet_hash(packet.packet_hash)
|
||||
stored_hash = True
|
||||
|
||||
Transport.transmit(interface, packet.raw)
|
||||
if not Transport.should_apply_delta(packet, interface): raw = packet.raw
|
||||
else:
|
||||
if packet.packet_type != RNS.Packet.ANNOUNCE: raw = Transport.mangle_hops(packet.raw, Transport.local_hops_delta)
|
||||
else: raw = Transport.mangle_hops(packet.raw, Transport.local_hops_delta, transport_insert=packet.header_type==RNS.Packet.HEADER_1)
|
||||
|
||||
Transport.transmit(interface, raw)
|
||||
if packet.packet_type == RNS.Packet.ANNOUNCE: interface.sent_announce()
|
||||
if packet.destination.type == RNS.Destination.PLAIN and packet.is_outbound_pr: interface.sent_path_request()
|
||||
packet_sent(packet)
|
||||
@@ -1327,6 +1352,17 @@ class Transport:
|
||||
|
||||
return sent
|
||||
|
||||
@staticmethod
|
||||
def should_apply_delta(packet, interface):
|
||||
return not Transport.owner.is_connected_to_shared_instance and packet.hops == 0 and Transport.local_hops_delta != 0 \
|
||||
and not packet.destination.type == RNS.Destination.PLAIN and not packet.destination.type == RNS.Destination.GROUP \
|
||||
and not interface in Transport.local_client_interfaces and not Transport.is_shared_instance(interface)
|
||||
|
||||
@staticmethod
|
||||
def mangle_hops(raw, hops, transport_insert=False):
|
||||
if not transport_insert: return raw[0:1]+struct.pack("!B", hops)+raw[2:]
|
||||
else: return struct.pack("!B", ord(raw[0:1]) | (RNS.Packet.HEADER_2<<6) | (Transport.TRANSPORT<<4))+struct.pack("!B", hops)+Transport.identity.hash+raw[2:]
|
||||
|
||||
@staticmethod
|
||||
def add_packet_hash(packet_hash):
|
||||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
@@ -1406,10 +1442,8 @@ class Transport:
|
||||
ifac = raw[2:2+interface.ifac_size]
|
||||
|
||||
# Generate mask
|
||||
mask = RNS.Cryptography.hkdf(length=len(raw),
|
||||
derive_from=ifac,
|
||||
salt=interface.ifac_key,
|
||||
context=None)
|
||||
mask = RNS.Cryptography.hkdf(length=len(raw), derive_from=ifac,
|
||||
salt=interface.ifac_key, context=None)
|
||||
# Unmask payload
|
||||
i = 0; unmasked_raw = b""
|
||||
for byte in raw:
|
||||
@@ -1511,9 +1545,13 @@ class Transport:
|
||||
# through a shared Reticulum instance
|
||||
from_local_client = (packet.receiving_interface in Transport.local_client_interfaces)
|
||||
for_local_client = (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.path_table and Transport.path_table[packet.destination_hash][IDX_PT_HOPS] == 0)
|
||||
for_local_client_link = (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.link_table and Transport.link_table[packet.destination_hash][IDX_LT_RCVD_IF] in Transport.local_client_interfaces)
|
||||
for_local_client_link |= (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.link_table and Transport.link_table[packet.destination_hash][IDX_LT_NH_IF] in Transport.local_client_interfaces)
|
||||
local_client_nh = (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.link_table and Transport.link_table[packet.destination_hash][IDX_LT_NH_IF] in Transport.local_client_interfaces)
|
||||
local_client_rh = (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.link_table and Transport.link_table[packet.destination_hash][IDX_LT_RCVD_IF] in Transport.local_client_interfaces)
|
||||
proof_for_local_client = (packet.destination_hash in Transport.reverse_table) and (Transport.reverse_table[packet.destination_hash][IDX_RT_RCVD_IF] in Transport.local_client_interfaces)
|
||||
to_local_client = for_local_client or proof_for_local_client
|
||||
instance_local_link = local_client_nh and local_client_rh
|
||||
for_local_client_link = local_client_nh or local_client_rh
|
||||
link_request_handled = False
|
||||
|
||||
# Plain broadcast packets from local clients are sent
|
||||
# directly on all attached interfaces, since they are
|
||||
@@ -1575,10 +1613,23 @@ class Transport:
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
|
||||
elif remaining_hops == 0:
|
||||
# Just increase hop count and transmit
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
if to_local_client and RNS.Transport.local_hops_delta != 0:
|
||||
if packet.header_type == RNS.Packet.HEADER_2:
|
||||
# Strip transport headers and transmit
|
||||
new_flags = (RNS.Packet.HEADER_1) << 6 | (Transport.BROADCAST) << 4 | (packet.flags & 0b00001111)
|
||||
new_raw = struct.pack("!B", new_flags)
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
|
||||
else:
|
||||
# Just increase hop count and transmit
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
else:
|
||||
# Just increase hop count and transmit
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
|
||||
outbound_interface = Transport.path_table[packet.destination_hash][IDX_PT_RVCD_IF]
|
||||
|
||||
@@ -1623,6 +1674,7 @@ class Transport:
|
||||
proof_timeout ] # 8: Proof timeout timestamp
|
||||
|
||||
with Transport.link_table_lock: Transport.link_table[RNS.Link.link_id_from_lr_packet(packet)] = link_entry
|
||||
link_request_handled = True
|
||||
|
||||
else:
|
||||
# Entry format is
|
||||
@@ -1632,6 +1684,7 @@ class Transport:
|
||||
|
||||
with Transport.reverse_table_lock: Transport.reverse_table[packet.getTruncatedHash()] = reverse_entry
|
||||
|
||||
if Transport.local_hops_delta != 0 and from_local_client and not to_local_client: new_raw = Transport.mangle_hops(new_raw, Transport.local_hops_delta)
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
with Transport.path_table_lock: Transport.path_table[packet.destination_hash][IDX_PT_TIMESTAMP] = time.time()
|
||||
|
||||
@@ -1675,7 +1728,7 @@ class Transport:
|
||||
Transport.add_packet_hash(packet.packet_hash)
|
||||
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += struct.pack("!B", packet.hops if not from_local_client or instance_local_link or Transport.local_hops_delta == 0 else Transport.local_hops_delta)
|
||||
new_raw += packet.raw[2:]
|
||||
Transport.transmit(outbound_interface, new_raw)
|
||||
Transport.link_table[packet.destination_hash][IDX_LT_TIMESTAMP] = time.time()
|
||||
@@ -1737,8 +1790,7 @@ class Transport:
|
||||
with Transport.announce_table_lock:
|
||||
if packet.destination_hash in Transport.announce_table: Transport.announce_table.pop(packet.destination_hash)
|
||||
|
||||
else:
|
||||
received_from = packet.destination_hash
|
||||
else: received_from = packet.destination_hash
|
||||
|
||||
# Check if this announce should be inserted into
|
||||
# announce and destination tables
|
||||
@@ -1755,6 +1807,7 @@ class Transport:
|
||||
random_blob = packet.data[RNS.Identity.KEYSIZE//8+RNS.Identity.NAME_HASH_LENGTH//8:RNS.Identity.KEYSIZE//8+RNS.Identity.NAME_HASH_LENGTH//8+10]
|
||||
random_blobs = []
|
||||
with Transport.inbound_announce_lock:
|
||||
announced_destination_known |= packet.destination_hash in Transport.path_table
|
||||
if announced_destination_known:
|
||||
random_blobs = Transport.path_table[packet.destination_hash][IDX_PT_RANDBLOBS]
|
||||
|
||||
@@ -1827,7 +1880,6 @@ class Transport:
|
||||
else:
|
||||
# If this destination is unknown in our table
|
||||
# we should add it
|
||||
Transport.mark_path_unknown_state(packet.destination_hash)
|
||||
should_add = True
|
||||
|
||||
if should_add:
|
||||
@@ -1944,17 +1996,10 @@ class Transport:
|
||||
if is_from_local_client and packet.context == RNS.Packet.PATH_RESPONSE:
|
||||
for local_interface in Transport.local_client_interfaces:
|
||||
if packet.receiving_interface != local_interface:
|
||||
new_announce = RNS.Packet(
|
||||
announce_destination,
|
||||
announce_data,
|
||||
RNS.Packet.ANNOUNCE, # <-- This one?
|
||||
context = announce_context,
|
||||
header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT,
|
||||
transport_id = Transport.identity.hash,
|
||||
attached_interface = local_interface,
|
||||
context_flag = packet.context_flag,
|
||||
)
|
||||
new_announce = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE, # <-- This one?
|
||||
context = announce_context, header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash,
|
||||
attached_interface = local_interface, context_flag = packet.context_flag)
|
||||
|
||||
new_announce.hops = packet.hops
|
||||
new_announce.send()
|
||||
@@ -1962,17 +2007,10 @@ class Transport:
|
||||
else:
|
||||
for local_interface in Transport.local_client_interfaces:
|
||||
if packet.receiving_interface != local_interface:
|
||||
new_announce = RNS.Packet(
|
||||
announce_destination,
|
||||
announce_data,
|
||||
RNS.Packet.ANNOUNCE,
|
||||
context = announce_context,
|
||||
header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT,
|
||||
transport_id = Transport.identity.hash,
|
||||
attached_interface = local_interface,
|
||||
context_flag = packet.context_flag,
|
||||
)
|
||||
new_announce = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE,
|
||||
context = announce_context, header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash,
|
||||
attached_interface = local_interface, context_flag = packet.context_flag)
|
||||
|
||||
new_announce.hops = packet.hops
|
||||
new_announce.send()
|
||||
@@ -1994,17 +2032,10 @@ class Transport:
|
||||
announce_context = RNS.Packet.NONE
|
||||
announce_data = packet.data
|
||||
|
||||
new_announce = RNS.Packet(
|
||||
announce_destination,
|
||||
announce_data,
|
||||
RNS.Packet.ANNOUNCE,
|
||||
context = RNS.Packet.PATH_RESPONSE,
|
||||
header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT,
|
||||
transport_id = Transport.identity.hash,
|
||||
attached_interface = attached_interface,
|
||||
context_flag = packet.context_flag,
|
||||
)
|
||||
new_announce = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE,
|
||||
context = RNS.Packet.PATH_RESPONSE, header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash,
|
||||
attached_interface = attached_interface, context_flag = packet.context_flag)
|
||||
|
||||
new_announce.hops = packet.hops
|
||||
new_announce.send()
|
||||
@@ -2021,8 +2052,7 @@ class Transport:
|
||||
# announce to the tunnels table
|
||||
if hasattr(packet.receiving_interface, "tunnel_id") and packet.receiving_interface.tunnel_id != None:
|
||||
with Transport.tunnels_lock:
|
||||
if not packet.receiving_interface.tunnel_id in Transport.tunnels:
|
||||
RNS.log(f"Tunnel ID for {packet.receiving_interface} was not found in tunnel table", RNS.LOG_WARNING)
|
||||
if not packet.receiving_interface.tunnel_id in Transport.tunnels: RNS.log(f"Tunnel ID for {packet.receiving_interface} was not found in tunnel table", RNS.LOG_WARNING)
|
||||
else:
|
||||
tunnel_entry = Transport.tunnels[packet.receiving_interface.tunnel_id]
|
||||
paths = tunnel_entry[IDX_TT_PATHS]
|
||||
@@ -2040,10 +2070,10 @@ class Transport:
|
||||
# the handlers aspect filter
|
||||
execute_callback = False
|
||||
announce_identity = RNS.Identity.recall(packet.destination_hash, _no_use=True)
|
||||
if handler.aspect_filter == None:
|
||||
# If the handlers aspect filter is set to
|
||||
# None, we execute the callback in all cases
|
||||
execute_callback = True
|
||||
|
||||
# If the handlers aspect filter is set to
|
||||
# None, we execute the callback in all cases
|
||||
if handler.aspect_filter == None: execute_callback = True
|
||||
else:
|
||||
handler_expected_hash = RNS.Destination.hash_from_name_and_identity(handler.aspect_filter, announce_identity)
|
||||
if packet.destination_hash == handler_expected_hash: execute_callback = True
|
||||
@@ -2088,7 +2118,7 @@ class Transport:
|
||||
RNS.trace_exception(e)
|
||||
|
||||
# Handling for link requests to local destinations
|
||||
elif packet.packet_type == RNS.Packet.LINKREQUEST:
|
||||
elif packet.packet_type == RNS.Packet.LINKREQUEST and not link_request_handled:
|
||||
if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
|
||||
destination = None
|
||||
with Transport.destinations_map_lock:
|
||||
@@ -2131,7 +2161,7 @@ class Transport:
|
||||
if packet.context == RNS.Packet.CACHE_REQUEST:
|
||||
cached_packet = Transport.get_cached_packet(packet.data)
|
||||
if cached_packet != None:
|
||||
cached_packet.unpack()
|
||||
if not cached_packet.unpack(): return
|
||||
RNS.Packet(destination=link, data=cached_packet.data,
|
||||
packet_type=cached_packet.packet_type, context=cached_packet.context).send()
|
||||
|
||||
@@ -2158,13 +2188,11 @@ class Transport:
|
||||
packet.destination = destination
|
||||
if destination.receive(packet):
|
||||
if destination.proof_strategy == RNS.Destination.PROVE_ALL: packet.prove()
|
||||
|
||||
elif destination.proof_strategy == RNS.Destination.PROVE_APP:
|
||||
if destination.callbacks.proof_requested:
|
||||
try:
|
||||
if destination.callbacks.proof_requested(packet): packet.prove()
|
||||
except Exception as e:
|
||||
RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
except Exception as e: RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
# Handling for proofs and link-request proofs
|
||||
elif packet.packet_type == RNS.Packet.PROOF:
|
||||
@@ -2191,16 +2219,14 @@ class Transport:
|
||||
if peer_identity.validate(signature, signed_data):
|
||||
RNS.log("Link request proof validated for transport via "+str(link_entry[IDX_LT_RCVD_IF]), RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += struct.pack("!B", packet.hops if not from_local_client or instance_local_link or Transport.local_hops_delta == 0 else Transport.local_hops_delta)
|
||||
new_raw += packet.raw[2:]
|
||||
Transport.link_table[packet.destination_hash][IDX_LT_VALIDATED] = True
|
||||
Transport.transmit(link_entry[IDX_LT_RCVD_IF], new_raw)
|
||||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
RNS.Identity._used_destination_data(link_entry[IDX_LT_DSTHASH])
|
||||
|
||||
else:
|
||||
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
|
||||
|
||||
else: RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
|
||||
except Exception as e: RNS.log("Could not transport link request proof. The contained exception was: "+str(e), RNS.LOG_DEBUG) if RNS.sl(LOG_DEBUG) else None
|
||||
else: RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
|
||||
else: RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
|
||||
@@ -2258,7 +2284,7 @@ class Transport:
|
||||
if packet.receiving_interface == reverse_entry[IDX_RT_OUTB_IF]:
|
||||
RNS.log("Proof received on correct interface, transporting it via "+str(reverse_entry[IDX_RT_RCVD_IF]), RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += struct.pack("!B", packet.hops if not from_local_client or proof_for_local_client or Transport.local_hops_delta == 0 else Transport.local_hops_delta)
|
||||
new_raw += packet.raw[2:]
|
||||
Transport.transmit(reverse_entry[IDX_RT_RCVD_IF], new_raw)
|
||||
else:
|
||||
@@ -2656,7 +2682,8 @@ class Transport:
|
||||
:returns: The destination hash as *bytes* for the next hop to the specified destination, or *None* if the next hop is unknown.
|
||||
"""
|
||||
with Transport.path_table_lock:
|
||||
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_NEXT_HOP]
|
||||
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_NEXT_HOP]
|
||||
elif destination_hash in Transport.destinations_map: return Transport.identity.hash
|
||||
else: return None
|
||||
|
||||
@staticmethod
|
||||
@@ -2666,7 +2693,8 @@ class Transport:
|
||||
:returns: The interface for the next hop to the specified destination, or *None* if the interface is unknown.
|
||||
"""
|
||||
with Transport.path_table_lock:
|
||||
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_RVCD_IF]
|
||||
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_RVCD_IF]
|
||||
elif destination_hash in Transport.destinations_map: return RNS.Reticulum.get_instance().shared_instance_interface
|
||||
else: return None
|
||||
|
||||
@staticmethod
|
||||
@@ -2915,7 +2943,8 @@ class Transport:
|
||||
if attached_interface != None:
|
||||
should_ingress_limit = attached_interface.should_ingress_limit_pr()
|
||||
if RNS.Reticulum.transport_enabled():
|
||||
if attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR: should_search_for_unknown = True
|
||||
if attached_interface.recursive_prs: should_search_for_unknown = True
|
||||
elif attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR: should_search_for_unknown = True
|
||||
|
||||
if RNS.sl(RNS.LOG_DEBUG):
|
||||
interface_str = f" on {attached_interface}"
|
||||
@@ -2952,7 +2981,7 @@ class Transport:
|
||||
RNS.log("Not answering path request on roaming-mode interface, since next hop is on same roaming-mode interface", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
|
||||
|
||||
else:
|
||||
packet.unpack()
|
||||
if not packet.unpack(): return
|
||||
packet.hops = Transport.path_table[destination_hash][IDX_PT_HOPS]
|
||||
|
||||
if requestor_transport_id != None and next_hop == requestor_transport_id:
|
||||
@@ -3054,25 +3083,24 @@ class Transport:
|
||||
def from_local_client(packet):
|
||||
if hasattr(packet.receiving_interface, "parent_interface"):
|
||||
return Transport.is_local_client_interface(packet.receiving_interface)
|
||||
else:
|
||||
return False
|
||||
else: return False
|
||||
|
||||
@staticmethod
|
||||
def is_local_client_interface(interface):
|
||||
if hasattr(interface, "parent_interface"):
|
||||
if hasattr(interface.parent_interface, "is_local_shared_instance"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
if hasattr(interface.parent_interface, "is_local_shared_instance"): return True
|
||||
else: return False
|
||||
else: return False
|
||||
|
||||
@staticmethod
|
||||
def is_shared_instance(interface):
|
||||
if hasattr(interface, "is_local_shared_instance"): return True
|
||||
else: return False
|
||||
|
||||
@staticmethod
|
||||
def interface_to_shared_instance(interface):
|
||||
if hasattr(interface, "is_connected_to_shared_instance"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if hasattr(interface, "is_connected_to_shared_instance"): return True
|
||||
else: return False
|
||||
|
||||
@staticmethod
|
||||
def detach_interfaces():
|
||||
|
||||
+16
-6
@@ -100,7 +100,7 @@ __example_rns_config__ = '''# This is an example Reticulum config file.
|
||||
# always-on. This directive is optional and can be removed
|
||||
# for brevity.
|
||||
|
||||
enable_transport = No
|
||||
enable_transport = no
|
||||
|
||||
|
||||
# By default, the first program to launch the Reticulum
|
||||
@@ -112,7 +112,7 @@ enable_transport = No
|
||||
# user, and should generally be turned on. This directive
|
||||
# is optional and can be removed for brevity.
|
||||
|
||||
share_instance = Yes
|
||||
share_instance = yes
|
||||
|
||||
|
||||
# If you want to run multiple *different* shared instances
|
||||
@@ -178,7 +178,7 @@ instance_name = default
|
||||
# the network. If this option is enabled, Reticulum will
|
||||
# collect interface information discovered from the network.
|
||||
|
||||
# discover_interfaces = No
|
||||
# discover_interfaces = no
|
||||
|
||||
|
||||
# If you only want to discover interfaces from specific
|
||||
@@ -215,7 +215,7 @@ instance_name = default
|
||||
# an optional directive, and can be left out for brevity.
|
||||
# This behaviour is disabled by default.
|
||||
|
||||
# panic_on_interface_error = No
|
||||
# panic_on_interface_error = no
|
||||
|
||||
|
||||
# When Transport is enabled, it is possible to allow the
|
||||
@@ -226,14 +226,15 @@ instance_name = default
|
||||
# Transport Instance, and printed to the log at startup.
|
||||
# Optional, and disabled by default.
|
||||
|
||||
# respond_to_probes = No
|
||||
# respond_to_probes = no
|
||||
|
||||
|
||||
# You can publish your local list of blackholed identities
|
||||
# for other transport instances to use for automatic,
|
||||
# network-wide blackhole management.
|
||||
|
||||
# publish_blackhole = No
|
||||
# publish_blackhole = no
|
||||
|
||||
|
||||
# List of remote transport identities from which to auto-
|
||||
# matically source lists of blackholed identities.
|
||||
@@ -248,12 +249,21 @@ instance_name = default
|
||||
|
||||
# blackhole_sources = 521c87a83afb8f29e4455e77930b973b
|
||||
|
||||
|
||||
# You can set the interval in minutes at which remote
|
||||
# blackhole sources are updated. Defaults to one hour.
|
||||
|
||||
# blackhole_update_interval = 60
|
||||
|
||||
|
||||
# When not running as a transport node, it is possible to
|
||||
# force the use of the same, static transport identity
|
||||
# for every instance start. Defaults to using a new identity
|
||||
# at every instance start, if transport is disabled.
|
||||
|
||||
# static_transport_identity = no
|
||||
|
||||
|
||||
[logging]
|
||||
# Valid log levels are 0 through 7:
|
||||
# 0: Log only critical information
|
||||
|
||||
@@ -423,6 +423,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ROAMING: modestr = "Roaming"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY: modestr = "Boundary"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_GATEWAY: modestr = "Gateway"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_INTERNAL: modestr = "Internal"
|
||||
else: modestr = "Full"
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "1.3.3"
|
||||
__version__ = "1.3.8"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
# Sphinx build info version 1
|
||||
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: af83acf0d95a344cb0386ad87adf5695
|
||||
config: 5f1bb7356498498f06570848af4171af
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1326,7 +1326,7 @@ Each document is a numbered directory containing:
|
||||
|
||||
**Nomad Network Interface**
|
||||
|
||||
When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
|
||||
When the Nomad Network page node is enabled, work documents are viewable through the nomadnet interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
|
||||
|
||||
Cryptographic Attribution
|
||||
-------------------------
|
||||
@@ -1490,4 +1490,23 @@ To view signature information for commits, use Git's standard ``--show-signature
|
||||
|
||||
Refactored module
|
||||
|
||||
The output shows whether the commit signature is valid, and whether the author field matches the signing identity.
|
||||
The output shows whether the commit signature is valid, and whether the author field matches the signing identity.
|
||||
|
||||
.. tip::
|
||||
If you want to display both the identity hash and LXMF address for authors, you can generate a ``.mailmap`` file that resolves identities to LXMF addresses with the following script:
|
||||
|
||||
.. code::
|
||||
#!/bin/bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
cd $DIR
|
||||
|
||||
id_regex="<([0-9a-f]{32})( .*)*>"
|
||||
extract_id="s/.*$id_regex/\1/g"
|
||||
|
||||
rm -f .mailmap
|
||||
git shortlog -se | grep -Ee "$id_regex" | sed -r "$extract_id" | while read -r id ; do
|
||||
if lxmf=$(rnid -i $id -H lxmf.delivery | grep -Ee "destination for this Identity is" | sed -r "$extract_id"); then
|
||||
echo "<$id lxmf:$lxmf> <$id>" >> .mailmap
|
||||
fi
|
||||
done
|
||||
@@ -1207,6 +1207,22 @@ These can be used to control various aspects of interface behaviour.
|
||||
link or a remote TCP tunnel) solely to discover better local
|
||||
infrastructure, which then supersedes the bootstrap interface.
|
||||
|
||||
|
||||
* | The ``recursive_prs`` option allows you to enable recursive path
|
||||
discovery on an interface regardless of its configured interface
|
||||
mode. When this option is enabled, Reticulum will attempt to
|
||||
recursively discover paths for path requests received on this
|
||||
interface.
|
||||
|
||||
|
||||
* | The ``announces_from_internal`` option controls whether any
|
||||
announces *received on* an ``internal`` mode interface will
|
||||
propagate *out* on the interface this option is set on. Note
|
||||
that this controls *announce propagation*; even if announces
|
||||
do not propagate out when received, paths to destinations on
|
||||
``internal`` mode interfaces may still be resolvable by means
|
||||
of path requests.
|
||||
|
||||
.. _interfaces-modes:
|
||||
|
||||
Interface Modes
|
||||
@@ -1284,6 +1300,15 @@ the default mode.
|
||||
public Transport Node available on the Internet, the interface
|
||||
connecting over the Internet should be set to ``boundary`` mode.
|
||||
|
||||
* | The ``internal`` mode designates interfaces that belong to a
|
||||
network different from any marked as ``boundary``. Announces from
|
||||
a ``boundary`` interface will not propagate to interfaces set as
|
||||
``internal``, but announces *will* propagate from ``internal`` *to*
|
||||
``boundary``. Devices on the ``internal`` side of the network will
|
||||
still be able to resolve paths to destinations across the boundary
|
||||
when needed, since recursive path requests are enabled for ``internal``
|
||||
mode interfaces by default.
|
||||
|
||||
For a table describing the impact of all modes on announce propagation,
|
||||
please see the :ref:`Announce Propagation Rules<understanding-announcepropagation>` section.
|
||||
|
||||
|
||||
@@ -707,32 +707,58 @@ Announce Propagation Rules
|
||||
|
||||
The following table illustrates the rules for automatically propagating announces from one interface type to another, for all possible combinations. For the purpose of announce propagation, the *Full* and *Gateway* modes are identical.
|
||||
|
||||
See the :ref:`Interface Modes<interfaces-modes>` section for a conceptual overview of the different interface modes, and how they are configured.
|
||||
|
||||
.. image:: graphics/if_mode_graph_b.png
|
||||
|
||||
See the :ref:`Interface Modes<interfaces-modes>` section for a conceptual overview of the different interface modes, and how they are configured.
|
||||
Or, represented in table form:
|
||||
|
||||
.. image:: graphics/if_mode_graph_c.png
|
||||
|
||||
..
|
||||
(.. code-block:: text)
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼───> Full >───┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✓ ──┼───> Full >───┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✓ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✕ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✕ ──┼────> AP >────┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✕ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼────> AP >────┼── ✓ ── Boundary
|
||||
Internal ── ✕ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Roaming >──┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✕ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼─> Roaming >──┼── ✕ ── Boundary
|
||||
Internal ── ✕ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Boundary >─┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✓ ──┼─> Boundary >─┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✕ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼─> Internal >─┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✓ ──┘ └── ✕ ── Roaming
|
||||
|
||||
dest → Full AP Boundary Roaming Gateway Internal
|
||||
source ↓
|
||||
Full ✓ ✕ ✓ ✓ ✓ ✓
|
||||
Gateway ✓ ✕ ✓ ✓ ✓ ✓
|
||||
AP ✓ ✕ ✓ ✓ ✓ ✓
|
||||
Boundary ✓ ✕ ✓ ✕ ✓ ✕
|
||||
Internal ✓ ✕ ✓ ✕ ✓ ✓
|
||||
Roaming ✓ ✕ ✕ ✕ ✓ ✓
|
||||
|
||||
✓ Will rebroadcast announce
|
||||
✕ Will not rebroadcast announce
|
||||
|
||||
|
||||
.. _understanding-primitives:
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ img[src*="if_mode_graph_b.png"] {
|
||||
background-color: rgb(169, 177, 186);
|
||||
}
|
||||
|
||||
img[src*="if_mode_graph_c.png"] {
|
||||
background-color: rgb(169, 177, 186);
|
||||
}
|
||||
|
||||
dt.sig {
|
||||
margin-bottom: 0.75rem;
|
||||
margin-top: 1.75rem;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const DOCUMENTATION_OPTIONS = {
|
||||
VERSION: '1.3.2',
|
||||
VERSION: '1.3.8',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Distributed Development - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Distributed Development - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -431,7 +431,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Code Examples - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Code Examples - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -3648,7 +3648,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -296,7 +296,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#"><link rel="search" title="Search" href="search.html">
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -202,7 +202,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -846,7 +846,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Getting Started Fast - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Getting Started Fast - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -776,7 +776,7 @@ section of this manual.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
+24
-6
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Git Over Reticulum - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Git Over Reticulum - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -1306,7 +1306,7 @@ adm:9710b86ba12c42d1d8f30f74fe509286
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">N</span></code> - Numbered comment files (msgpack format)</p></li>
|
||||
</ul>
|
||||
<p><strong>Nomad Network Interface</strong></p>
|
||||
<p>When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.</p>
|
||||
<p>When the Nomad Network page node is enabled, work documents are viewable through the nomadnet interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.</p>
|
||||
</section>
|
||||
<section id="cryptographic-attribution">
|
||||
<h3>Cryptographic Attribution<a class="headerlink" href="#cryptographic-attribution" title="Link to this heading">¶</a></h3>
|
||||
@@ -1437,6 +1437,24 @@ Date: Mon Jan 15 09:30:00 2026 +0100
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The output shows whether the commit signature is valid, and whether the author field matches the signing identity.</p>
|
||||
<div class="admonition tip">
|
||||
<p class="admonition-title">Tip</p>
|
||||
<p>If you want to display both the identity hash and LXMF address for authors, you can generate a <code class="docutils literal notranslate"><span class="pre">.mailmap</span></code> file that resolves identities to LXMF addresses with the following script:</p>
|
||||
<div class="highlight-#!/bin/bash notranslate"><div class="highlight"><pre><span></span>DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
cd $DIR
|
||||
|
||||
id_regex="<([0-9a-f]{32})( .*)*>"
|
||||
extract_id="s/.*$id_regex/\1/g"
|
||||
|
||||
rm -f .mailmap
|
||||
git shortlog -se | grep -Ee "$id_regex" | sed -r "$extract_id" | while read -r id ; do
|
||||
if lxmf=$(rnid -i $id -H lxmf.delivery | grep -Ee "destination for this Identity is" | sed -r "$extract_id"); then
|
||||
echo "<$id lxmf:$lxmf> <$id>" >> .mailmap
|
||||
fi
|
||||
done
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
@@ -1576,7 +1594,7 @@ Date: Mon Jan 15 09:30:00 2026 +0100
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Communications Hardware - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Communications Hardware - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -676,7 +676,7 @@ can be used with Reticulum. This includes virtual software modems such as
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -719,7 +719,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Configuring Interfaces - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Configuring Interfaces - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -1361,6 +1361,24 @@ link or a remote TCP tunnel) solely to discover better local
|
||||
infrastructure, which then supersedes the bootstrap interface.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">recursive_prs</span></code> option allows you to enable recursive path
|
||||
discovery on an interface regardless of its configured interface
|
||||
mode. When this option is enabled, Reticulum will attempt to
|
||||
recursively discover paths for path requests received on this
|
||||
interface.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">announces_from_internal</span></code> option controls whether any
|
||||
announces <em>received on</em> an <code class="docutils literal notranslate"><span class="pre">internal</span></code> mode interface will
|
||||
propagate <em>out</em> on the interface this option is set on. Note
|
||||
that this controls <em>announce propagation</em>; even if announces
|
||||
do not propagate out when received, paths to destinations on
|
||||
<code class="docutils literal notranslate"><span class="pre">internal</span></code> mode interfaces may still be resolvable by means
|
||||
of path requests.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
@@ -1451,6 +1469,17 @@ public Transport Node available on the Internet, the interface
|
||||
connecting over the Internet should be set to <code class="docutils literal notranslate"><span class="pre">boundary</span></code> mode.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">internal</span></code> mode designates interfaces that belong to a
|
||||
network different from any marked as <code class="docutils literal notranslate"><span class="pre">boundary</span></code>. Announces from
|
||||
a <code class="docutils literal notranslate"><span class="pre">boundary</span></code> interface will not propagate to interfaces set as
|
||||
<code class="docutils literal notranslate"><span class="pre">internal</span></code>, but announces <em>will</em> propagate from <code class="docutils literal notranslate"><span class="pre">internal</span></code> <em>to</em>
|
||||
<code class="docutils literal notranslate"><span class="pre">boundary</span></code>. Devices on the <code class="docutils literal notranslate"><span class="pre">internal</span></code> side of the network will
|
||||
still be able to resolve paths to destinations across the boundary
|
||||
when needed, since recursive path requests are enabled for <code class="docutils literal notranslate"><span class="pre">internal</span></code>
|
||||
mode interfaces by default.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
<p>For a table describing the impact of all modes on announce propagation,
|
||||
@@ -1774,7 +1803,7 @@ interface basis under the relevant interface configuration section.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Reticulum License - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Reticulum License - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -345,7 +345,7 @@ SOFTWARE.
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Building Networks - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Building Networks - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -664,7 +664,7 @@ differently than a mobile device roaming between radio cells.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
Binary file not shown.
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>API Reference - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>API Reference - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -2519,7 +2519,7 @@ will announce it.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>Search - Reticulum Network Stack 1.3.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<title>Search - Reticulum Network Stack 1.3.8 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -304,7 +304,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Programs Using Reticulum - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Programs Using Reticulum - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -513,7 +513,7 @@ plugin system for expandability.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Support Reticulum - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Support Reticulum - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -383,7 +383,7 @@ circumstances, so we rely on old-fashioned human feedback.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -969,8 +969,10 @@ but excluding any interface access codes.
|
||||
<section id="announce-propagation-rules">
|
||||
<span id="understanding-announcepropagation"></span><h3>Announce Propagation Rules<a class="headerlink" href="#announce-propagation-rules" title="Link to this heading">¶</a></h3>
|
||||
<p>The following table illustrates the rules for automatically propagating announces from one interface type to another, for all possible combinations. For the purpose of announce propagation, the <em>Full</em> and <em>Gateway</em> modes are identical.</p>
|
||||
<img alt="_images/if_mode_graph_b.png" src="_images/if_mode_graph_b.png" />
|
||||
<p>See the <a class="reference internal" href="interfaces.html#interfaces-modes"><span class="std std-ref">Interface Modes</span></a> section for a conceptual overview of the different interface modes, and how they are configured.</p>
|
||||
<img alt="_images/if_mode_graph_b.png" src="_images/if_mode_graph_b.png" />
|
||||
<p>Or, represented in table form:</p>
|
||||
<img alt="_images/if_mode_graph_c.png" src="_images/if_mode_graph_c.png" />
|
||||
</section>
|
||||
<section id="cryptographic-primitives">
|
||||
<span id="understanding-primitives"></span><h3>Cryptographic Primitives<a class="headerlink" href="#cryptographic-primitives" title="Link to this heading">¶</a></h3>
|
||||
@@ -1116,7 +1118,7 @@ those risks are acceptable to you.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -1639,7 +1639,7 @@ systemctl --user enable rnsd.service
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>What is Reticulum? - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>What is Reticulum? - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -505,7 +505,7 @@ network, and vice versa.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
|
||||
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
|
||||
<title>Zen of Reticulum - Reticulum Network Stack 1.3.2 documentation</title>
|
||||
<title>Zen of Reticulum - Reticulum Network Stack 1.3.8 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=e1715074" />
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.3.8 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -204,7 +204,7 @@
|
||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||
</div>
|
||||
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.2 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.3.8 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">
|
||||
@@ -677,7 +677,7 @@ Imagine a messaging app. You write it once. It works on a laptop connected to fi
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=8ca9e7a0"></script>
|
||||
</div><script src="_static/documentation_options.js?v=3cbadadc"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
||||
|
||||
@@ -1250,7 +1250,7 @@ Each document is a numbered directory containing:
|
||||
|
||||
**Nomad Network Interface**
|
||||
|
||||
When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
|
||||
When the Nomad Network page node is enabled, work documents are viewable through the nomadnet interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
|
||||
|
||||
### Cryptographic Attribution
|
||||
|
||||
|
||||
@@ -1136,6 +1136,20 @@ These can be used to control various aspects of interface behaviour.
|
||||
> link or a remote TCP tunnel) solely to discover better local
|
||||
> infrastructure, which then supersedes the bootstrap interface.
|
||||
>
|
||||
> * The `recursive_prs` option allows you to enable recursive path
|
||||
> discovery on an interface regardless of its configured interface
|
||||
> mode. When this option is enabled, Reticulum will attempt to
|
||||
> recursively discover paths for path requests received on this
|
||||
> interface.
|
||||
>
|
||||
> * The `announces_from_internal` option controls whether any
|
||||
> announces *received on* an `internal` mode interface will
|
||||
> propagate *out* on the interface this option is set on. Note
|
||||
> that this controls *announce propagation*; even if announces
|
||||
> do not propagate out when received, paths to destinations on
|
||||
> `internal` mode interfaces may still be resolvable by means
|
||||
> of path requests.
|
||||
>
|
||||
|
||||
## Interface Modes
|
||||
|
||||
@@ -1211,6 +1225,15 @@ the default mode.
|
||||
> public Transport Node available on the Internet, the interface
|
||||
> connecting over the Internet should be set to `boundary` mode.
|
||||
>
|
||||
> * The `internal` mode designates interfaces that belong to a
|
||||
> network different from any marked as `boundary`. Announces from
|
||||
> a `boundary` interface will not propagate to interfaces set as
|
||||
> `internal`, but announces *will* propagate from `internal` *to*
|
||||
> `boundary`. Devices on the `internal` side of the network will
|
||||
> still be able to resolve paths to destinations across the boundary
|
||||
> when needed, since recursive path requests are enabled for `internal`
|
||||
> mode interfaces by default.
|
||||
>
|
||||
|
||||
For a table describing the impact of all modes on announce propagation,
|
||||
please see the [Announce Propagation Rules](understanding.md#understanding-announcepropagation) section.
|
||||
|
||||
@@ -632,30 +632,55 @@ but excluding any interface access codes.
|
||||
|
||||
The following table illustrates the rules for automatically propagating announces from one interface type to another, for all possible combinations. For the purpose of announce propagation, the *Full* and *Gateway* modes are identical.
|
||||
|
||||

|
||||
|
||||
See the [Interface Modes](interfaces.md#interfaces-modes) section for a conceptual overview of the different interface modes, and how they are configured.
|
||||
|
||||

|
||||
|
||||
Or, represented in table form:
|
||||
|
||||

|
||||
<!-- (.. code-block:: text)
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼───> Full >───┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✓ ──┼───> Full >───┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✓ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✕ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✕ ──┼────> AP >────┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✕ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼────> AP >────┼── ✓ ── Boundary
|
||||
Internal ── ✕ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Roaming >──┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✕ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼─> Roaming >──┼── ✕ ── Boundary
|
||||
Internal ── ✕ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Boundary >─┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming -->
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✓ ──┼─> Boundary >─┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✕ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼─> Internal >─┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✓ ──┘ └── ✕ ── Roaming
|
||||
|
||||
dest → Full AP Boundary Roaming Gateway Internal
|
||||
source ↓
|
||||
Full ✓ ✕ ✓ ✓ ✓ ✓
|
||||
Gateway ✓ ✕ ✓ ✓ ✓ ✓
|
||||
AP ✓ ✕ ✓ ✓ ✓ ✓
|
||||
Boundary ✓ ✕ ✓ ✕ ✓ ✕
|
||||
Internal ✓ ✕ ✓ ✕ ✓ ✓
|
||||
Roaming ✓ ✕ ✕ ✕ ✓ ✓
|
||||
|
||||
✓ Will rebroadcast announce
|
||||
✕ Will not rebroadcast announce -->
|
||||
|
||||
### Cryptographic Primitives
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ img[src*="if_mode_graph_b.png"] {
|
||||
background-color: rgb(169, 177, 186);
|
||||
}
|
||||
|
||||
img[src*="if_mode_graph_c.png"] {
|
||||
background-color: rgb(169, 177, 186);
|
||||
}
|
||||
|
||||
dt.sig {
|
||||
margin-bottom: 0.75rem;
|
||||
margin-top: 1.75rem;
|
||||
|
||||
+21
-2
@@ -1326,7 +1326,7 @@ Each document is a numbered directory containing:
|
||||
|
||||
**Nomad Network Interface**
|
||||
|
||||
When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
|
||||
When the Nomad Network page node is enabled, work documents are viewable through the nomadnet interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
|
||||
|
||||
Cryptographic Attribution
|
||||
-------------------------
|
||||
@@ -1490,4 +1490,23 @@ To view signature information for commits, use Git's standard ``--show-signature
|
||||
|
||||
Refactored module
|
||||
|
||||
The output shows whether the commit signature is valid, and whether the author field matches the signing identity.
|
||||
The output shows whether the commit signature is valid, and whether the author field matches the signing identity.
|
||||
|
||||
.. tip::
|
||||
If you want to display both the identity hash and LXMF address for authors, you can generate a ``.mailmap`` file that resolves identities to LXMF addresses with the following script:
|
||||
|
||||
.. code::
|
||||
#!/bin/bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
cd $DIR
|
||||
|
||||
id_regex="<([0-9a-f]{32})( .*)*>"
|
||||
extract_id="s/.*$id_regex/\1/g"
|
||||
|
||||
rm -f .mailmap
|
||||
git shortlog -se | grep -Ee "$id_regex" | sed -r "$extract_id" | while read -r id ; do
|
||||
if lxmf=$(rnid -i $id -H lxmf.delivery | grep -Ee "destination for this Identity is" | sed -r "$extract_id"); then
|
||||
echo "<$id lxmf:$lxmf> <$id>" >> .mailmap
|
||||
fi
|
||||
done
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 35 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
@@ -1207,6 +1207,22 @@ These can be used to control various aspects of interface behaviour.
|
||||
link or a remote TCP tunnel) solely to discover better local
|
||||
infrastructure, which then supersedes the bootstrap interface.
|
||||
|
||||
|
||||
* | The ``recursive_prs`` option allows you to enable recursive path
|
||||
discovery on an interface regardless of its configured interface
|
||||
mode. When this option is enabled, Reticulum will attempt to
|
||||
recursively discover paths for path requests received on this
|
||||
interface.
|
||||
|
||||
|
||||
* | The ``announces_from_internal`` option controls whether any
|
||||
announces *received on* an ``internal`` mode interface will
|
||||
propagate *out* on the interface this option is set on. Note
|
||||
that this controls *announce propagation*; even if announces
|
||||
do not propagate out when received, paths to destinations on
|
||||
``internal`` mode interfaces may still be resolvable by means
|
||||
of path requests.
|
||||
|
||||
.. _interfaces-modes:
|
||||
|
||||
Interface Modes
|
||||
@@ -1284,6 +1300,15 @@ the default mode.
|
||||
public Transport Node available on the Internet, the interface
|
||||
connecting over the Internet should be set to ``boundary`` mode.
|
||||
|
||||
* | The ``internal`` mode designates interfaces that belong to a
|
||||
network different from any marked as ``boundary``. Announces from
|
||||
a ``boundary`` interface will not propagate to interfaces set as
|
||||
``internal``, but announces *will* propagate from ``internal`` *to*
|
||||
``boundary``. Devices on the ``internal`` side of the network will
|
||||
still be able to resolve paths to destinations across the boundary
|
||||
when needed, since recursive path requests are enabled for ``internal``
|
||||
mode interfaces by default.
|
||||
|
||||
For a table describing the impact of all modes on announce propagation,
|
||||
please see the :ref:`Announce Propagation Rules<understanding-announcepropagation>` section.
|
||||
|
||||
|
||||
@@ -707,32 +707,58 @@ Announce Propagation Rules
|
||||
|
||||
The following table illustrates the rules for automatically propagating announces from one interface type to another, for all possible combinations. For the purpose of announce propagation, the *Full* and *Gateway* modes are identical.
|
||||
|
||||
See the :ref:`Interface Modes<interfaces-modes>` section for a conceptual overview of the different interface modes, and how they are configured.
|
||||
|
||||
.. image:: graphics/if_mode_graph_b.png
|
||||
|
||||
See the :ref:`Interface Modes<interfaces-modes>` section for a conceptual overview of the different interface modes, and how they are configured.
|
||||
Or, represented in table form:
|
||||
|
||||
.. image:: graphics/if_mode_graph_c.png
|
||||
|
||||
..
|
||||
(.. code-block:: text)
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼───> Full >───┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✓ ──┼───> Full >───┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✓ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✕ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✕ ──┼────> AP >────┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✕ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼────> AP >────┼── ✓ ── Boundary
|
||||
Internal ── ✕ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Roaming >──┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✕ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼─> Roaming >──┼── ✕ ── Boundary
|
||||
Internal ── ✕ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Boundary >─┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✓ ──┼─> Boundary >─┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✕ ── Internal
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┤ ├── ✕ ── AP
|
||||
Boundary ── ✕ ──┼─> Internal >─┼── ✓ ── Boundary
|
||||
Internal ── ✓ ──┤ ├── ✓ ── Internal
|
||||
Roaming ─── ✓ ──┘ └── ✕ ── Roaming
|
||||
|
||||
dest → Full AP Boundary Roaming Gateway Internal
|
||||
source ↓
|
||||
Full ✓ ✕ ✓ ✓ ✓ ✓
|
||||
Gateway ✓ ✕ ✓ ✓ ✓ ✓
|
||||
AP ✓ ✕ ✓ ✓ ✓ ✓
|
||||
Boundary ✓ ✕ ✓ ✕ ✓ ✕
|
||||
Internal ✓ ✕ ✓ ✕ ✓ ✓
|
||||
Roaming ✓ ✕ ✕ ✕ ✓ ✓
|
||||
|
||||
✓ Will rebroadcast announce
|
||||
✕ Will not rebroadcast announce
|
||||
|
||||
|
||||
.. _understanding-primitives:
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user