Compare commits

...

261 Commits

Author SHA1 Message Date
Mark Qvist b17175dfef Updated changelog 2024-05-05 19:57:48 +02:00
Mark Qvist 1103784997 Updated documentation 2024-05-05 19:56:33 +02:00
Mark Qvist d2feb8b136 Improved path response logic 2024-05-04 21:57:03 +02:00
Mark Qvist f595648a9b Updated tests 2024-05-04 20:27:27 +02:00
Mark Qvist b06f5285c5 Fix LR proof delivery on unknown hop count paths 2024-05-04 20:27:04 +02:00
Mark Qvist 8330f70a27 Fixed link packet routing in topologies where transport packets leak to non-intended instances in the link chain 2024-05-04 19:52:02 +02:00
Mark Qvist 15e10b9435 Added expected hops property to link class 2024-05-04 19:15:57 +02:00
Mark Qvist b91c852330 Updated path request timing 2024-05-04 16:19:04 +02:00
Mark Qvist 75acdf5902 Updated version 2024-05-03 23:49:39 +02:00
Mark Qvist dae40f2684 Removed T3S3 build from autoinstaller 2024-05-03 18:20:17 +02:00
Mark Qvist 4edacf82f3 Merge branch 'master' of github.com:markqvist/Reticulum 2024-05-03 16:22:37 +02:00
markqvist 4b0a0668a5 Update Contributing.md 2024-05-01 17:50:15 +02:00
markqvist a52af17123 Merge pull request #495 from jschulthess/master
optionally load identity file from file in Echo and Link examples
2024-05-01 17:28:10 +02:00
Mark Qvist 0b0a3313c5 Multicast address type modifications 2024-05-01 15:49:48 +02:00
markqvist 34af2e7af7 Merge pull request #476 from thiaguetz/feat/multicast-address-type
feat: implement multicast address type definition on AutoInterface configuration
2024-05-01 15:44:03 +02:00
Jürg Schulthess 12bf7977d2 fix comment 2024-04-29 08:25:40 +02:00
Jürg Schulthess b69b939d6f realign with upstream 2024-04-29 08:10:48 +02:00
Jürg Schulthess b5556f664b realign with upstream 2024-04-29 08:07:22 +02:00
Jürg Schulthess f804ba0263 explicit exit not needed 2024-04-29 08:04:04 +02:00
Jürg Schulthess 84a1ab0ca3 add option to load identity from file 2024-04-29 07:59:55 +02:00
markqvist 465695b9ae Merge pull request #490 from nothingbutlucas/master
docs: Fix a typo. startig / starting
2024-04-22 01:33:10 +02:00
Mark Qvist a999a4a250 Added support for T3S3 boards to rnodeconf autoinstaller 2024-04-22 01:26:35 +02:00
nothingbutlucas cbb5d99280 docs: Fix a typo. startig / starting
Signed-off-by: nothingbutlucas <69118979+nothingbutlucas@users.noreply.github.com>
2024-04-21 16:11:03 -03:00
Mark Qvist 64f5192c79 Changed rnodeconf autoinstaller menu order 2024-04-20 22:25:57 +02:00
Mark Qvist d223ebc8c0 Added rnodeconf autoinstaller support for Heltec LoRa32 V3 boards 2024-04-20 22:03:14 +02:00
markqvist c28f413fe6 Merge pull request #486 from cobraPA/upstream_add_heltec_v3
Add product and model, plus support for Heltec V3 serial only setup to rnodeconf.
2024-04-20 18:54:09 +02:00
Kevin Brosius 92e5f65887 Add product and model, plus support for Heltec V3 serial only setup
to rnodeconf.
2024-04-11 01:41:50 -04:00
Mark Qvist b977f33df6 Display error on unknown model capabilities instead of fail 2024-03-28 12:05:30 +01:00
Mark Qvist 589fcb8201 Added custom EEPROM bootstrap to rnodeconf 2024-03-28 00:04:48 +01:00
Mark Qvist e5427d70ac Added custom EEPROM bootstrap to rnodeconf 2024-03-27 21:48:32 +01:00
Mark Qvist 2f5381b307 Added TCXO model code comment 2024-03-24 11:51:44 +01:00
Thiaguetz 11baace08d feat: implement multicast address type definition on AutoInterface configuration 2024-03-23 00:54:56 -03:00
Mark Qvist a4d5b5cb17 Merge branch 'master' of github.com:markqvist/Reticulum 2024-03-19 11:52:58 +01:00
Mark Qvist 9cb181690e Added link getter to resource advertisement class 2024-03-19 11:52:32 +01:00
markqvist ff6604290e Update LICENSE 2024-03-10 22:14:29 +01:00
markqvist 2dbd3cbc0f Update Contributing.md 2024-03-10 22:14:03 +01:00
markqvist 2a11097cac Update Contributing.md 2024-03-10 22:13:33 +01:00
markqvist c0e3181ae3 Update Contributing.md 2024-03-10 22:11:44 +01:00
markqvist 5a0316ae7f Update Contributing.md 2024-03-10 20:39:49 +01:00
Mark Qvist 177bb62610 Updated changelog 2024-03-09 21:09:06 +01:00
Mark Qvist 7cd3cde398 Updated changelog 2024-03-09 21:08:17 +01:00
Mark Qvist 29bdcea616 Updated manual 2024-03-09 21:05:59 +01:00
Mark Qvist d9460c43ad Updated version 2024-03-09 21:01:12 +01:00
markqvist fb02e980db Merge pull request #461 from attermann/firmware_repos
Support for alternate download URL for custom firmware images
2024-03-08 01:10:34 +01:00
Mark Qvist 4947463440 Updated roadmap 2024-03-06 12:14:36 +01:00
Chad Attermann 5565349255 Fixed installation of alternate firmware version
Required version info was not being downloaded when alternate (not latest)
version is selected rsulting in the error "Could not read locally cached
release information."
2024-03-05 19:02:47 -07:00
Chad Attermann 1b7b131adc Added support for alternate firmware download URL
New command line option `--fw-url` accepts an alternate URL to use for
downloading firmware images.
Note this feature is moderately opinionated when it comes to directory
structure. The intent is to be compatible with GitHub releases, so the
latest version info is expected to be found at
"{fw-url}latest/download/release.json" and firmware images at
"{fw-url}download/{version}/{firmware_file.zip}".
2024-03-05 17:14:52 -07:00
Mark Qvist ace0d997d4 Updated changelog 2024-03-02 00:40:44 +01:00
Mark Qvist 798c252284 Updated manual 2024-03-02 00:40:35 +01:00
Mark Qvist 7da22c8580 Updated documentation build 2024-03-01 00:47:12 +01:00
Mark Qvist eefbb89cde Updated version 2024-03-01 00:05:40 +01:00
Mark Qvist 18f50ff1ae Limit amount of random blobs kept in memory and persisted to disk. Add check for non-existent announce in processing table. 2024-03-01 00:03:56 +01:00
Mark Qvist 05e97ac0db Fixed saving known destination when on-disk storage file has become corrupted 2024-02-29 23:23:41 +01:00
Mark Qvist c2c3a144d2 Added payload data inactivity metric to Link API 2024-02-29 23:05:16 +01:00
markqvist ea369015ee Update issue templates 2024-02-29 17:07:53 +01:00
markqvist 9745842862 Update issue templates 2024-02-29 17:05:46 +01:00
markqvist 246289c52d Create config.yml 2024-02-29 17:04:17 +01:00
markqvist ff71cb2f98 Update issue templates 2024-02-29 16:58:57 +01:00
Mark Qvist 5ca1ef1777 Revert EEPROM check logic 2024-02-29 16:18:39 +01:00
Mark Qvist 2b764b4af8 Allow EEPROM checksum mismatch on autoinstall. Fixes #432. 2024-02-29 15:50:45 +01:00
Mark Qvist a62843cd75 Updated readme 2024-02-16 17:54:31 +01:00
Mark Qvist 633435390d Added ability to flash T3 boards with TCXO 2024-02-16 17:32:01 +01:00
Mark Qvist 1e207ef972 Updated readme 2024-02-16 17:31:42 +01:00
Mark Qvist 35e9a0b38a Updated changelog 2024-02-14 16:58:51 +01:00
Mark Qvist 3d7f3825fb Updated manual 2024-02-14 16:54:29 +01:00
Mark Qvist 04b67a545d Updated version 2024-02-13 19:01:07 +01:00
Mark Qvist 61c2fbd0da Merge branch 'master' of github.com:markqvist/Reticulum 2024-02-13 19:00:00 +01:00
Mark Qvist 1aba4ec43a Added support for SX126x-based RNodes 2024-02-13 18:59:23 +01:00
markqvist 841a3daa26 Merge pull request #439 from jacobeva/master
Update min and max values to support SX1280
2024-02-09 22:30:32 +01:00
jacob.eva d98f03f245 Update min and max values to support SX1280 2024-02-09 21:17:58 +00:00
Mark Qvist 878e67f69d Fixed invalid RSSI offset reference. Fixes #433. 2024-01-18 23:01:54 +01:00
Mark Qvist e582a6d6d1 Updated changelog 2024-01-17 22:59:02 +01:00
Mark Qvist a948afb816 Updated manual 2024-01-17 22:56:24 +01:00
Mark Qvist 86a294388f Merge branch 'master' of github.com:markqvist/Reticulum 2024-01-17 22:52:48 +01:00
Mark Qvist 429a0b1bd3 Updated changelog 2024-01-17 22:52:01 +01:00
Mark Qvist ee8bb42633 Updated manual 2024-01-17 22:51:16 +01:00
Mark Qvist c659388a2c Updated manual 2024-01-17 22:36:17 +01:00
markqvist eaa8199988 Merge pull request #428 from jacobeva/master
Add NRF52 support
2024-01-17 01:33:07 +01:00
jacob.eva 4f890e7e8a Added NRF52 support 2024-01-16 21:30:31 +00:00
Mark Qvist a37e039424 Check input_file attribut 2024-01-14 18:57:23 +01:00
Mark Qvist 8e1e2a9c54 Added debug function 2024-01-14 18:56:20 +01:00
Mark Qvist e4f94c9d0b Updated docs 2024-01-14 18:55:44 +01:00
Mark Qvist b007530123 Adjusted resource timeout calculation 2024-01-14 01:06:43 +01:00
Mark Qvist 4066bba303 Merge branch 'master' of github.com:markqvist/Reticulum 2024-01-14 00:48:14 +01:00
Mark Qvist 8951517d01 Updated version 2024-01-14 00:47:45 +01:00
Mark Qvist ae1d962b9b Fixed large resource transfers failing under some conditions 2024-01-14 00:46:55 +01:00
Mark Qvist a2caa47334 Improved link tests 2024-01-14 00:12:30 +01:00
Mark Qvist 9f43da9105 Fixed rnprobe formatting issue 2024-01-13 16:37:48 +01:00
Mark Qvist 038c696db9 Fixed missing check on malformed advertisement packets 2024-01-13 16:36:11 +01:00
Mark Qvist 8fa6ec144c Updated readme 2024-01-03 12:05:30 +01:00
Mark Qvist a8ccff7c55 Updated contribution guidelines 2024-01-03 12:00:10 +01:00
markqvist a5783da407 Merge pull request #416 from jooray/patch-2
Fix typo
2023-12-31 12:24:48 +01:00
Juraj Bednar bec3cee425 Fix typo 2023-12-30 23:47:51 +01:00
Mark Qvist b15bd19de5 Added funding info 2023-12-30 22:00:46 +01:00
Mark Qvist 38390fd021 Updated license 2023-12-30 21:57:40 +01:00
Mark Qvist 40e0eee64f Updated license 2023-12-30 21:55:20 +01:00
Mark Qvist af4cbb1baf Added funding info 2023-12-30 21:53:50 +01:00
Mark Qvist d3f4192fe3 Added funding info 2023-12-30 21:52:41 +01:00
Mark Qvist 47ef62ac11 Updated contribution guidelines 2023-12-30 21:43:35 +01:00
Mark Qvist d15ddc7a49 Updated contribution guidelines 2023-12-30 17:34:51 +01:00
Mark Qvist d67c8eb1cd Fixed potential division by zero 2023-12-25 11:39:24 +01:00
Mark Qvist f4de5d5199 Updated changelog 2023-12-07 15:52:20 +01:00
Mark Qvist 34e42988ea Updated docs 2023-12-07 15:51:22 +01:00
Mark Qvist 81d5d41149 Updated changelog 2023-12-07 15:51:15 +01:00
Mark Qvist 6b3f3a37f0 Updated version 2023-12-06 00:07:06 +01:00
Mark Qvist 60a604f635 Carrier change flag on listener replace 2023-12-06 00:06:45 +01:00
Mark Qvist 55a2daf379 Updated docs 2023-12-02 02:14:49 +01:00
Mark Qvist 2dbde13321 Added identity import and export in hex, base32 and base64 formats 2023-12-02 02:10:22 +01:00
Mark Qvist 6620dcde6b Updated docs 2023-11-14 10:06:28 +01:00
Mark Qvist 60966d5bb1 Updated changelog 2023-11-14 10:06:19 +01:00
Mark Qvist ea22a53bf2 Updated docs 2023-11-13 23:38:46 +01:00
Mark Qvist 7b9526b4ed Updated version 2023-11-13 23:23:40 +01:00
Mark Qvist 676074187a Added timeout and wait options to rnprobe and improved output formatting 2023-11-13 23:22:58 +01:00
Mark Qvist 5dd2c31caf Generate receipts prior to raw transmit 2023-11-13 23:12:59 +01:00
Mark Qvist 2db400a1a0 Updated changelog 2023-11-13 23:11:29 +01:00
Mark Qvist b68dbaf15e Updated log levels 2023-11-08 15:23:29 +01:00
Mark Qvist 84febcdf95 Updated changelog 2023-11-06 11:28:22 +01:00
Mark Qvist c972ef90c8 Updated manual 2023-11-06 11:21:32 +01:00
Mark Qvist 19a74e3130 Updated changelog 2023-11-06 11:21:23 +01:00
Mark Qvist 5ba789f782 Updated single-packet timing 2023-11-06 11:10:38 +01:00
Mark Qvist 58b5501e17 Cleanup 2023-11-06 11:08:31 +01:00
Mark Qvist b584832b8f Fixed logging error messages when a local client connects while instance is starting up 2023-11-06 11:06:14 +01:00
Mark Qvist fc0cf17c4d Updated docs 2023-11-05 23:37:45 +01:00
Mark Qvist 001dd369ec Updated version 2023-11-05 23:37:38 +01:00
Mark Qvist 9ce2ea4a5c Updated link test 2023-11-05 23:36:19 +01:00
Mark Qvist eec8814c22 Updated version 2023-11-05 23:29:06 +01:00
Mark Qvist 7a6ed68482 Set socket options 2023-11-05 22:57:03 +01:00
Mark Qvist cd9e23f2de Updated manual 2023-11-04 23:19:08 +01:00
Mark Qvist ffa84de0bc Updated changelog 2023-11-04 23:18:59 +01:00
Mark Qvist 89d3cdba17 Updated docs 2023-11-04 18:13:26 +01:00
Mark Qvist 2ba5843f22 Updated version 2023-11-04 18:05:42 +01:00
Mark Qvist c4d0f08767 Improved resource transfers over unreliable links 2023-11-04 18:05:20 +01:00
Mark Qvist db1cdec2a2 Fixed premature request timeout 2023-11-04 17:59:27 +01:00
Mark Qvist 1eea1a6a22 Updated example 2023-11-04 17:56:20 +01:00
Mark Qvist 4a69ce5a98 Updated changelog 2023-11-02 21:44:48 +01:00
Mark Qvist 8d653cba9b Updated docs 2023-11-02 21:39:57 +01:00
Mark Qvist a6126a6bc5 Updated version 2023-11-02 21:37:16 +01:00
Mark Qvist 957c2b3bc1 Fixed invalid reference 2023-11-02 21:33:21 +01:00
Mark Qvist 494bde4e79 Updated docs 2023-11-02 18:53:22 +01:00
Mark Qvist 5e39136dff Fixed missing path state resetting on stale path rediscovery 2023-11-02 16:15:42 +01:00
Mark Qvist 4b26a86a73 Added probe count option to rnprobe 2023-11-02 16:14:38 +01:00
Mark Qvist 43a6e280c0 Fixed bluetooth read timeouts on Android in environments with hight 2.4G noise 2023-11-02 16:08:49 +01:00
Mark Qvist 237a45b2ca Don't send rediscovery requests on local originator 2023-11-02 13:33:12 +01:00
Mark Qvist b161650ced Adjusted link timings 2023-11-02 13:04:09 +01:00
Mark Qvist 24975eac31 Updated version 2023-11-02 13:03:53 +01:00
Mark Qvist 5d1ff36565 Updated docs 2023-11-02 13:03:39 +01:00
Mark Qvist 628777900e Fixed attribute 2023-11-02 12:44:57 +01:00
Mark Qvist 12e87425dc Adjusted timings 2023-11-02 12:24:42 +01:00
Mark Qvist 873f049e20 Fixed redundant rediscovery path request 2023-11-02 04:35:57 +01:00
Mark Qvist 2ea963ed03 Fixed missing timeout calculation 2023-11-02 04:35:10 +01:00
Mark Qvist 1d1276d6dd Updated changelog 2023-10-31 12:24:59 +01:00
Mark Qvist 83741724b0 Updated documentation 2023-10-31 12:24:18 +01:00
Mark Qvist a4143cfe6d Improved link error handling. Fixes #387. 2023-10-31 11:44:12 +01:00
Mark Qvist 3d645ae2f4 Updated documentation 2023-10-31 11:09:54 +01:00
Mark Qvist 5ba125c801 Updated documentation 2023-10-31 10:53:43 +01:00
Mark Qvist badb392898 Updated manual 2023-10-28 00:40:07 +02:00
Mark Qvist c0e1ce8d86 Updated documentation and manual 2023-10-28 00:28:41 +02:00
markqvist 0bc248c5e4 Merge pull request #385 from jschulthess/master
Add user systemd service to manual
2023-10-28 00:23:10 +02:00
Mark Qvist 798dfb1727 Added ability to query physical layer stats on links 2023-10-28 00:05:35 +02:00
Mark Qvist a451b987aa Updated documentation 2023-10-28 00:03:53 +02:00
Mark Qvist f01074e5b8 Implemented link establishment on ultra low bandwidth links 2023-10-27 18:16:52 +02:00
Mark Qvist 0e12442a28 Local interface bitrate simulation 2023-10-27 18:12:53 +02:00
Jürg Schulthess a4e8489a34 fix code text syntax 2023-10-25 14:09:24 +02:00
Jürg Schulthess 276b6fbd22 fix indentation 2023-10-25 14:07:34 +02:00
Jürg Schulthess 52ab08c289 add user systemd service 2023-10-25 13:31:37 +02:00
Mark Qvist 38236366cf Improved pretty print output 2023-10-24 13:24:40 +02:00
Mark Qvist af3cc3c5dd Updated version 2023-10-24 01:45:07 +02:00
Mark Qvist 35ed1f950c Updated version 2023-10-24 01:43:50 +02:00
Mark Qvist c050ef945e Updated pretty-print functions 2023-10-24 01:41:49 +02:00
Mark Qvist bed71fa3f8 Added physical layer link stats to link and packet classes 2023-10-24 01:41:12 +02:00
Mark Qvist cf125daf5c Added link quality calculation to RNode interface 2023-10-24 01:40:17 +02:00
Mark Qvist 9f425c2e8d Updated exceptions 2023-10-24 01:39:25 +02:00
Mark Qvist 0dc78241ac Updated version 2023-10-19 01:39:47 +02:00
Mark Qvist 01e963e891 Updated manual 2023-10-19 01:39:39 +02:00
Mark Qvist b3731524ac Improved path re-discovery in changing topographies 2023-10-19 00:38:41 +02:00
Mark Qvist 67c7395ea7 Improved shared interface reconnection on service restart 2023-10-18 23:18:59 +02:00
Mark Qvist fddf36a920 Updated manual 2023-10-16 19:33:13 +02:00
Mark Qvist 4f561a8c0c Added exception handling to interface detach 2023-10-16 18:54:36 +02:00
Mark Qvist 778d6105c1 Updated readme 2023-10-10 00:32:15 +02:00
Mark Qvist 60c94dc9b6 Updated readme 2023-10-10 00:29:40 +02:00
Mark Qvist f71395e449 Updated readme 2023-10-10 00:26:28 +02:00
Mark Qvist 1abacca9bf Fixed missing command definition 2023-10-08 18:02:38 +02:00
Mark Qvist 40281d5403 Updated changelog 2023-10-07 16:42:10 +02:00
Mark Qvist e0da489156 Updated manual 2023-10-07 16:33:54 +02:00
Mark Qvist 2dcf1350e7 Updated changelog 2023-10-07 16:33:45 +02:00
Mark Qvist 1e280611ce Updated documentation and manuals 2023-10-07 13:02:42 +02:00
Mark Qvist f1d107846f Updated version 2023-10-07 13:00:16 +02:00
Mark Qvist cc951dcb53 Added RPC key configuration option to manual 2023-10-07 12:40:30 +02:00
Mark Qvist b5856a3706 Added configuration option to specify shared instance RPC key 2023-10-07 12:34:10 +02:00
Mark Qvist ed3479da9a Reordered airtime stats 2023-10-04 23:46:35 +02:00
Mark Qvist 5e15f421b7 Updated manual 2023-10-02 18:01:28 +02:00
Mark Qvist 0a9366ba6e Updated Android log level on bluetooth failure 2023-10-02 17:39:19 +02:00
Mark Qvist cf31435f39 Updated docs 2023-10-02 17:36:52 +02:00
Mark Qvist 9f58860842 Added missing super init on Android interfaces 2023-10-02 17:36:33 +02:00
Mark Qvist 875348383d Updated roadmap 2023-10-01 23:46:01 +02:00
Mark Qvist f79f190525 Changed ir utility name to rnir. Closes #377. 2023-10-01 23:39:43 +02:00
Mark Qvist 5e27a81412 Updated changelog 2023-10-01 12:41:45 +02:00
Mark Qvist 0dcb009579 Updated docs and manual 2023-10-01 12:34:50 +02:00
Mark Qvist 943f76804b Updated utility documentation 2023-10-01 12:34:29 +02:00
Mark Qvist 8bbe6ae3ae Updated docs and manual 2023-10-01 12:09:49 +02:00
Mark Qvist f0d85dd078 Merge branch 'master' of github.com:markqvist/Reticulum 2023-10-01 11:46:57 +02:00
Mark Qvist f85dda1829 Fixed typos in examples 2023-10-01 11:46:30 +02:00
markqvist 91e064cdf1 Merge pull request #375 from connervieira/patch-1
Fixed some typos
2023-10-01 11:46:25 +02:00
Mark Qvist fb4e53f6e3 Configured announce ingress limit defaults 2023-10-01 11:39:24 +02:00
Mark Qvist 03340ed091 Added ability to drop all paths via a specific transport instance to rnpath 2023-10-01 11:39:07 +02:00
Mark Qvist ed424fa0a2 Updated documentation 2023-10-01 09:51:27 +02:00
Mark Qvist 406ab216d1 Updated documentation 2023-10-01 09:24:25 +02:00
Mark Qvist 00d8a2064d Fixed typos 2023-10-01 09:24:17 +02:00
Mark Qvist 38b920e393 Updated docs and manual 2023-10-01 01:59:22 +02:00
Mark Qvist 1ed000c4d9 Updated manual 2023-10-01 01:35:17 +02:00
Mark Qvist d360958d10 Updated documentation 2023-10-01 01:35:00 +02:00
Mark Qvist fcdb455d73 Added sort mode to rnstatus 2023-10-01 01:08:19 +02:00
Mark Qvist 575639b721 Updated documentation 2023-10-01 01:08:08 +02:00
Mark Qvist 492573f9fe Added ingress control interface configuraion options 2023-10-01 00:43:26 +02:00
Mark Qvist c5d30f8ee6 Cleanup 2023-10-01 00:24:03 +02:00
Mark Qvist 3c4791a622 Implemented announce ingress control 2023-10-01 00:16:32 +02:00
Mark Qvist 803a5736c9 Added held announce stats to rnstatus 2023-10-01 00:12:49 +02:00
Mark Qvist 267ffbdf5f Updated version 2023-09-30 22:37:43 +02:00
Mark Qvist 52028aa44c Added ingress control config option 2023-09-30 21:07:22 +02:00
Mark Qvist c5248d53d6 Fixed frequency pretty print function 2023-09-30 19:22:39 +02:00
Mark Qvist 2d2f0947ac Fixed frequency pretty print function 2023-09-30 19:18:30 +02:00
Mark Qvist 4fa616a326 Added interface sorting and announce rate display to rnstatus 2023-09-30 19:14:39 +02:00
Mark Qvist 136713eec1 Added announce frequency stats 2023-09-30 19:13:58 +02:00
Mark Qvist 0fd75cb819 Added announce frequency sampling to interfaces 2023-09-30 19:11:10 +02:00
Mark Qvist ea52153969 Added convenience function for printing frequencies 2023-09-30 19:09:26 +02:00
Mark Qvist 3854781028 Updated manual 2023-09-30 19:08:57 +02:00
Conner Vieira ec2805f357 Fixed some typos 2023-09-29 20:54:48 -04:00
Mark Qvist b5cb3a65dd Fixed announce queue not clearing all announces with exceeded retry limit at the same time 2023-09-30 00:25:47 +02:00
Mark Qvist c79cb3aa20 Resolver skeleton 2023-09-29 23:18:30 +02:00
Mark Qvist 8bff119691 Added Identity Resolver skeleton 2023-09-29 12:44:03 +02:00
Mark Qvist 5e0b2c5b42 Allow rnid aspect lengths of 1 2023-09-29 12:29:37 +02:00
Mark Qvist 8908022b88 Updated license headers 2023-09-29 10:31:20 +02:00
Mark Qvist b0dda0ed86 Added Resolver class 2023-09-29 10:31:00 +02:00
Mark Qvist 6ae72d4225 Updated exit codes 2023-09-29 10:30:19 +02:00
Mark Qvist 0a188a2d39 Fixed output formatting in rncp 2023-09-25 15:29:41 +02:00
Mark Qvist 036abb28fe Added timeout option to rnprobe 2023-09-25 15:27:24 +02:00
Mark Qvist a732767a28 Fixed local RSSI and SNR cache pop order 2023-09-25 14:17:58 +02:00
Mark Qvist 32a1261d98 Updated manual 2023-09-22 12:01:17 +02:00
Mark Qvist 27c5af3bbc Updated manual 2023-09-22 10:07:10 +02:00
Mark Qvist 5872108da3 Added timeout to rnprobe 2023-09-22 10:04:37 +02:00
Mark Qvist 8f6c6b76de Updated changelog 2023-09-21 21:24:26 +02:00
Mark Qvist 99db625c62 Updated manual 2023-09-21 21:23:28 +02:00
Mark Qvist fdf6a31cbd Updated changelog 2023-09-21 21:23:19 +02:00
Mark Qvist 75f353d7e2 Updated documentation 2023-09-21 19:12:34 +02:00
Mark Qvist 82f204fb44 Added ability to enable a built-in probe responder destination for Transport Instances 2023-09-21 18:48:08 +02:00
Mark Qvist 8d4492ecfd Updated documentation 2023-09-21 18:47:40 +02:00
Mark Qvist f8a53458d6 Added respond_to_probes option to example config 2023-09-21 18:33:14 +02:00
Mark Qvist 4229837170 Updated documentation 2023-09-21 18:32:46 +02:00
Mark Qvist 4be2ae6c70 Fixed verbose output bug in rnprobe 2023-09-21 18:32:36 +02:00
Mark Qvist dbdeba2fe0 Updated rnprobe utility 2023-09-21 17:49:14 +02:00
Mark Qvist 7e34b61f37 Added link status check on identify 2023-09-21 14:12:32 +02:00
Mark Qvist bf726ed2c7 Fixed missing timeout check in rncp 2023-09-21 14:12:14 +02:00
Mark Qvist fa54a2affe Updated documentation 2023-09-21 13:51:03 +02:00
Mark Qvist 62e1d0e554 Updated version 2023-09-21 13:46:51 +02:00
Mark Qvist 9c823a038b Impproved path re-discovery on Transport Instances when local nodes roam to other network segments 2023-09-21 13:46:28 +02:00
Mark Qvist 1e6cd50f46 Updated rnstatus output 2023-09-21 12:07:11 +02:00
Mark Qvist 06716e4873 Disabled caching until redesign 2023-09-21 12:05:37 +02:00
Mark Qvist 8e4a1e3ffa Increased AutoInterface peering timeout on Android 2023-09-20 00:53:51 +02:00
Mark Qvist 0abb3bd4c3 Update changelog 2023-09-19 18:46:28 +02:00
Mark Qvist 336574daed Updated manual 2023-09-19 18:46:23 +02:00
Mark Qvist 07938ba111 Added ability to set custom RNode display address to rnodeconf 2023-09-19 18:33:37 +02:00
89 changed files with 16789 additions and 1056 deletions
+11
View File
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: ✨ Feature Request or Idea
url: https://github.com/markqvist/Reticulum/discussions/new?category=ideas
about: Propose and discuss features and ideas
- name: 💬 Questions, Help & Discussion
about: Ask anything, or get help
url: https://github.com/markqvist/Reticulum/discussions/new/choose
- name: 📖 Read the Reticulum Manual
url: https://markqvist.github.io/Reticulum/manual/
about: The complete documentation for Reticulum
+35
View File
@@ -0,0 +1,35 @@
---
name: "\U0001F41B Bug Report"
about: Report a reproducible bug
title: ''
labels: ''
assignees: ''
---
**Read the Contribution Guidelines**
Before creating a bug report on this issue tracker, you **must** read the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md). Issues that do not follow the contribution guidelines **will be deleted without comment**.
- The issue tracker is used by developers of this project. **Do not use it to ask general questions, or for support requests**.
- Ideas and feature requests can be made on the [Discussions](https://github.com/markqvist/Reticulum/discussions). **Only** feature requests accepted by maintainers and developers are tracked and included on the issue tracker. **Do not post feature requests here**.
- After reading the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md), delete this section from your bug report.
**Describe the Bug**
A clear and concise description of what the bug is.
**To Reproduce**
Describe in detail how to reproduce the bug.
**Expected Behavior**
A clear and concise description of what you expected to happen.
**Logs & Screenshots**
Please include any relevant log output. If applicable, also add screenshots to help explain your problem.
**System Information**
- OS and version
- Python version
- Program version
**Additional context**
Add any other context about the problem here.
+273 -3
View File
@@ -1,3 +1,272 @@
### 2024-05-05: RNS β 0.7.4
This maintenance release fixes a number of bugs, improves path requests and responses, and adds several useful features and capabilities. Thanks to @cobraPA, @jsculthess, @thiaguetz and @nothingbutlucas who contributed to this release!
**Changes**
- Added support for flashing and autoinstalling Heltec V3 boards to `rnodeconf`
- Added custom EEPROM bootstrapping capabilities to `rnodeconf`
- Added ability to load identities from file to Echo and Link examples
- Added ability to specify multicast address type in AutoInterface configuration
- Added link getter to resource advertisement class
- Improved path response logic and timing
- Improved path request timing
- Fixed a bug in Link Request proof delivery on unknown hop count paths
- Fixed broken link packet routing in topologies where transport packets leak to non-intended instances in the link chain
- Fixed typos in documentation
**Release Hashes**
```
f5c35f1b8720778eb508b687d66334d01b4ab266b2d8c2bc186702220dcaae29 rns-0.7.4-py3-none-any.whl
9eaa7170f97dad49551136965d3fcc971b56b1c2eda48c24b9ffd58d71daa016 rnspure-0.7.4-py3-none-any.whl
```
### 2024-03-09: RNS β 0.7.3
This release adds the ability to specify custom firmware URLs for flashing boards with `rnodeconf`. Thanks to @attermann who contributed to this release!
**Changes**
- Added ability to specify custom firmware URLs for flashing boards with `rnodeconf`
**Release Hashes**
```
bb24445ae9a3a63d348e4d7fe80b750608f257851b97b38fadab929b7a774bc9 rns-0.7.3-py3-none-any.whl
1b148d013103c35ba9a8e105082ef50686c130676d0a560ed709cb546129287e rnspure-0.7.3-py3-none-any.whl
```
### 2024-03-02: RNS β 0.7.2
This maintenance release improves memory consumption, fixes a few bugs, and adds ability to flash new boards with `rnodeconf`.
**Changes**
- Added ability to flash new boards with `rnodeconf`, including T3 boards with TCXOs
- Improved memory consumption on Transport Instances with many interfaces
- Fixed a bug that could cause the on-disk known destinations store to become corrupted
**Release Hashes**
```
3ce3ba80d5ae8d19c6b55bd51f44bd4beccbcea31554cb1f0d65428e4587b3d6 rns-0.7.2-py3-none-any.whl
83f914aaba2a8929a8cee95830a847e190197232a0cca4e7b906b15c6bbf8296 rnspure-0.7.2-py3-none-any.whl
```
### 2024-02-14: RNS β 0.7.1
This release adds support for RNodes based on SX1262, SX1268 and SX1280 modems, and fixes a number of bugs. Thanks to @jacobeva, who contributed to this release!
**Changes**
- Added support for SX1262, SX1268 and SX1280 based RNodes
- Updated `rnodeconf` to allow flashing T-Beam devices with SX126x chips
- Fixed an invalid RSSI offset reference
**Release Hashes**
```
8ecfbb42b6a699fd4ac5374ab5640e4bb164e80bb9ab4401ea82da132e497877 rns-0.7.1-py3-none-any.whl
e0ab487305ba1aee2d16044640e7eb72d332bbf51aeb0b8bf984d037a64cb493 rnspure-0.7.1-py3-none-any.whl
```
### 2024-01-17: RNS β 0.7.0
This maintenance release fixes a number of bugs. Thanks to @jooray and @jacobeva, who contributed to this release!
**Changes**
- Fixed large resource transfers failing under some conditions
- Fixed a potential division by zero
- Fixed a missing check on malformed advertisement packets
- Fixed a formatting issue in `rnprobe`
- Improved resource timeout calculations
**Release Hashes**
```
0dc2abe5373b9afadfba7ec05bf7ddeff659c004aa339a94001ebed5b46f5b47 rns-0.7.0-py3-none-any.whl
97f6e65a20b53bbdccd54b4d2bdaa36dc1712e144a55f40800c63fe7113819a5 rnspure-0.7.0-py3-none-any.whl
```
### 2023-12-07: RNS β 0.6.9
This release adds a few convenience functions to the `rnid` utility, and improves roaming support on Android.
**Changes**
- Added identity import and export in hex, base32 and base64 formats to the `rnid` utility.
- Added better carrier change detection for AutoInterface on Android.
**Release Hashes**
```
258daf22cb6e72c6cd04fe94447daedf51dfd968eb2f3370eab9c71ad0898dd0 rns-0.6.9-py3-none-any.whl
3644b64af5b4efd3969172bf0cf95ae1afba6c8ea99ce47d8e49e31a832bbaf8 rnspure-0.6.9-py3-none-any.whl
```
### 2023-11-14: RNS β 0.6.8
This maintenance release fixes a single bug.
**Bugfixes**
- Fixed packet receipts not being initialised in time for arriving proofs on fast interfaces
**Release Hashes**
```
3ffb01f3f45e35105ea30e60e5e493ba50528df38b4ea62672c9e1c093073b1c rns-0.6.8-py3-none-any.whl
de372814082ef7db59f4b2745b1f22b2ef9d97815190ec16c0596ba20406e0fb rnspure-0.6.8-py3-none-any.whl
```
### 2023-11-06: RNS β 0.6.7
This maintenance release improves tranport performance and fixes a logging bug.
**Changes**
- Improved local and remote transport performance by approximately 6x on faster links
- Significantly decreased latency over faster links
**Bugfixes**
- Fixed logging an error message when local clients connect while shared instance is still starting up
**Release Hashes**
```
c37dd1f59e037841f69ec518deecdae6719f978947de2473f04e7d95247805ac rns-0.6.7-py3-none-any.whl
1e2dcb44ec7271a4d26180db138fc54dce6d0d3cf3f816432d4d6a4b1cf83868 rnspure-0.6.7-py3-none-any.whl
```
### 2023-11-04: RNS β 0.6.6
This maintenance release improves transfers over unreliable links and fixes a bug in requests.
**Changes**
- Improved reliability of resource transfers over very slow and unreliable links
**Bugfixes**
- Fixed a bug that could cause requests to timeout prematurely
**Release Hashes**
```
b1127745750a43cd7389212d31aa09ccc735ab2d69e3b80bd28874f10082c322 rns-0.6.6-py3-none-any.whl
bf5ba5da4f37b93c14817367952cda63787ec88bbe601e41c13fcbb3fc22b6b6 rnspure-0.6.6-py3-none-any.whl
```
### 2023-11-02: RNS β 0.6.5
This release fixes a bug in path rediscovery for shared instance clients.
**Bugfixes**
- Fixed a bug in path rediscovery for shared instance clients
**Release Hashes**
```
5d54a5cfebe907c759351357a8f7d771670c895ff57f1325bf7fec42bcb46ba3 rns-0.6.5-py3-none-any.whl
accd2855e18ff06455b9454957388089e293073ec7093c64dee0dc7aa46ecb46 rnspure-0.6.5-py3-none-any.whl
```
### 2023-11-02: RNS β 0.6.4
This release fixes a number of bugs that had crept in while adding the new ultra low bandwidth link timing and faster path rediscovery mechanisms.
**Changes**
- Adjusted link timings for better support of very slow mediums
- Adjusted bluetooth read timeouts to account for occasional high latency in congested 2.4GHz environments
- Added a probe count option to the `rnprobe` utility.
**Bugfixes**
- Fixed a missing timeout calculation
- Fixed a redundant path request on path rediscovery
- Fixed missing path state resetting on stale path rediscovery
- Fixed a bug that could cause an attribute to be uninitialised
**Release Hashes**
```
566c725f68aa154eaca0880c894a39503027bf91714f17691e51d047800444c0 rns-0.6.4-py3-none-any.whl
a3a447fd40bf02fdb982523de0e4e9933e8e4cd4d4bd478254ea7dcac29e3fc1 rnspure-0.6.4-py3-none-any.whl
```
### 2023-10-31: RNS β 0.6.3
This release brings a series of under-the-hood reliability improvements and bugfixes. But most notably, Reticulum can now establish links over even ultra low bandwidth mediums, all the way down to 5 bits per second.
Thanks to @jschulthess, who contributed to this release!
**Changes**
- Implemented link establishment on ultra low bandwidth links
- Added link quality calculations to RNode interfaces
- Added physical layer link stats to Link and Packet classes
- Added userspace service documentation to the manual
- Improved path rediscovery in quickly changing topographies
- Improved shared interface reconnection on service restart
- Improved exception handling on interface detachment
- Updated formatted print functions
**Bugfixes**
- Fixed a missing USB command definition in the RNode interface driver
- Fixed a bug in link error handling that could cause an interface to detach
**Release Hashes**
```
1f54d4c6ff7ab7721089cbee6630783765f65efd51312879c0d3e5bee3ceab2f rns-0.6.3-py3-none-any.whl
5a90840f0fc9f1a62a3c37b514fb6222fd701a30024275dae8bcc27e29d40f25 rnspure-0.6.3-py3-none-any.whl
```
### 2023-10-07: RNS β 0.6.2
This maintenance release adds the ability to specify the shared instance RPC key in the Reticulum config file, making it possible to use all Reticulum functionality in the terminal on Android.
**Changes**
- Added configuration option to specify shared instance RPC key
- Reordered airtime stats in `rnstatus`
- Updated log levels on Android
**Bugfixes**
- Adding missing superclass init on Android interfaces
**Release Hashes**
```
a9958ad90f34f344003e18077f7abd3fa85666a39dc0cae8580071820dee13f9 rns-0.6.2-py3-none-any.whl
e68e8837d35d1a07a82c4b0e9db50ceace737a650e6e7e9ce2d9a013fd28f529 rnspure-0.6.2-py3-none-any.whl
```
### 2023-10-01: RNS β 0.6.1
This release brings a number of bugfixes, along with useful new mechanisms for ensuring network stability under high, non-constructive and unusual announce load situation.
**Changes**
- Added announce ingress rate control for new and unknown destinations
- Added per-interface announce frequency monitoring to the transport engine
- Added per-interface announce burst hold queues
- Added announce frequency statistics to `rnstatus`
- Added option to sort `rnstatus` output according to various metrics
- Added timeout options to `rnprobe`
- Added ability to drop all paths via a specific transport instance to `rnpath`
- Added new options and features to documentation and manual
**Bugfixes**
- Fixed announce queue not clearing all announces with exceeded retry limit at the same time
- Fixed a bug that caused local packet RSSI and SNR cache to get stuck
- Fixed output formatting in `rncp`
- Fixed `rnid` not allowing single-aspect destination names
- Fixed a number of typos in the documentation
**Release Hashes**
```
461e5cafa7560dcd3ec047141d10f0f48f151c36e1af1d65ec6c65f732cea46a rns-0.6.1-py3-none-any.whl
be6a4a6069f2d050e21582f2cf9d3bb59ed4040a0f07761a540bd752d90ea591 rnspure-0.6.1-py3-none-any.whl
```
#### 2023-09-21: RNS β 0.6.0
This release brings a few performance improvements, additions to the included utilities, and fixes a number of bugs.
**Changes**
- Added ability to run automatic probe responder on Transport Instances
- Improved `rnprobe` utility
- Improved AutoInterface peering on Android devices
- Improved Transport performance
- Improved path re-discovery when local nodes roam to other network segments
- Updated various parts of the documentation
**Bugfixes**
- Fixed missing timeout check in `rncp`
- Fixed missing link status check on `Identify()` call, which could lead to an unnecessary exception
**Release Hashes**
```
88a26b1593e82a628dab96dbe8820548aea0159235f730fa992bf1833db59246 rns-0.6.0-py3-none-any.whl
bcee416e4fb52346d01f6e0c46b1cebf84b127cc516603367fc2ae00a4149fa2 rnspure-0.6.0-py3-none-any.whl
```
### 2023-09-19: RNS β 0.5.9
This release brings major efficiency improvements to `Channel` and `Buffer` classes, adds a range of usability improvements to the included utilities and fixes a number of bugs.
@@ -11,6 +280,7 @@ This release brings major efficiency improvements to `Channel` and `Buffer` clas
- Added allowed_identities file support to `rncp`
- Added Transport Instance uptime to `rnstatus` output
- Added channel CSMA parameter stats to RNode Interface `rnstatus` output
- Added ability to set custom RNode OLED display address with rnodeconf
**Bugfixes**
- Fixed inadverdent AutoInterface multi-IF deque hit for resource transfer retries
@@ -20,8 +290,8 @@ This release brings major efficiency improvements to `Channel` and `Buffer` clas
**Release Hashes**
```
670bf8aec55a424001aa9ffc97648edf3db80c85b545aa372ab221fc3db26f1f rns-0.5.9-py3-none-any.whl
7a9ef70b3843dff886bd8d8dcbc7ba4a34f4278d789ceef0b704397d6b6af1f6 rnspure-0.5.9-py3-none-any.whl
207ab20bd68bab16b417fbd41a4ecdbcf1e2f6fa553d48df6c8fc181b6e84dac rns-0.5.9-py3-none-any.whl
93f0965567dfc2c43f3d703481fe1a7d7b1b8d0b3837ad41c37f28a8af5c1acc rnspure-0.5.9-py3-none-any.whl
```
### 2023-09-14: RNS β 0.5.8
@@ -875,4 +1145,4 @@ This was the first publicly available pre-release alpha of Reticulum.
### 2016-05-29: Inintial Repository Commit
The first commit to the Reticulum reference implementation was 9a9630cfd29e11ace3f12716ddb4dff0e5419b4b, which occurred on Sunday, the 22nd of May 2016.
The first commit to the Reticulum reference implementation was 9a9630cfd29e11ace3f12716ddb4dff0e5419b4b, which occurred on Sunday, the 29th of May 2016.
+27 -7
View File
@@ -2,22 +2,42 @@
Welcome, and thank you for your interest in contributing to Reticulum!
Apart from writing code, there are many ways in which you can contribute. Before getting started, please read these guidelines.
Apart from writing code, there are many ways in which you can contribute. Before interacting with this community, read these short and simple guidelines.
## Expected Conduct
First and foremost, there is one simple requirement for taking part in this community: While we primarily interact virtually, your actions matter and have real consequences. Therefore: **Act like a responsible, civilized person** - also in the face of disputes and heated disagreements. Speak your mind here, discussions are welcome. Just do so in the spirit of being face-to-face with everyone else. Thank you.
## Asking Questions
If you want to ask a question, do not open an issue.
If you want to ask a question, **do not open an issue**. The issue tracker is used by people *working on Reticulum* to track bugs, issues and improvements.
Instead, ask away on the [discussions](https://github.com/markqvist/Reticulum/discussions) or on the [Reticulum Matrix channel](https://unsigned.io/contact.html#reticulum:matrix.org) at `#reticulum:matrix.org`
Instead, ask away on the [discussions](https://github.com/markqvist/Reticulum/discussions) or on the [Reticulum Matrix channel](https://matrix.to/#/#reticulum:matrix.org) at `#reticulum:matrix.org`
## Providing Feedback
## Providing Feedback & Ideas
Likewise, feedback, ideas and feature requests are a very welcome way to contribute, and should also be posted on the [discussions](https://github.com/markqvist/Reticulum/discussions), or on the [Reticulum Matrix channel](https://unsigned.io/contact.html#reticulum:matrix.org) at `#reticulum:matrix.org`
Likewise, feedback, ideas and feature requests are a very welcome way to contribute, and should also be posted on the [discussions](https://github.com/markqvist/Reticulum/discussions), or on the [Reticulum Matrix channel](https://matrix.to/#/#reticulum:matrix.org) at `#reticulum:matrix.org`.
Please do not post feature requests or general ideas on the issue tracker, or in direct messages to the primary developers. You are much more likely to get a response and start a constructive discussion by posting your ideas in the public channels created for these purposes.
## Reporting Issues
If you have found a bug or issue in Reticulum, please report it on the [issue tracker](https://github.com/markqvist/Reticulum/issues).
If you have found a bug or issue in this project, please report it using the [issue tracker](https://github.com/markqvist/Reticulum/issues). If at all possible, be sure to include details on how to reproduce the bug.
Anything submitted to the issue tracker that does not follow these guidelines will be closed and removed without comments or explanation.
## Writing Code
If you are interested in contributing code, fixing open issues or adding features, please coordinate the effort with the maintainer or one of the main developers first, to ensure your efforts are in alignment with the [Roadmap](./Roadmap.md) and current development focus.
If you are interested in contributing code, fixing open issues or adding features, please coordinate the effort with the maintainer or one of the main developers **before** submitting a pull request. Before deciding to contribute, it is also a good idea to ensure your efforts are in alignment with the [Roadmap](./Roadmap.md) and current development focus.
Pull requests have a high chance of being accepted if they are:
- In alignment with the [Roadmap](./Roadmap.md) or solve an open issue or feature request
- Sufficiently tested to work with all API functions, and pass the standard test suite
- Functionally and conceptually complete and well-designed
- Not simply formatting or code style changes
- Well-documented
Even new ideas and proposals that have not been approved by a maintainer, or fall outside the established roadmap, are *occasionally* accepted - if they possess the remaining of the above qualities. If not, they will be closed and removed without comments or explanation.
By contributing code to this project, you agree that copyright for the code is transferred to the Reticulum maintainers and that the code is irrevocably placed under the [MIT license](./LICENSE).
+2 -2
View File
@@ -32,7 +32,7 @@ def program_setup(configpath):
# Destinations are endpoints in Reticulum, that can be addressed
# and communicated with. Destinations can also announce their
# existence, which will let the network know they are reachable
# and autoomatically create paths to them, from anywhere else
# and automatically create paths to them, from anywhere else
# in the network.
destination_1 = RNS.Destination(
identity,
@@ -53,7 +53,7 @@ def program_setup(configpath):
)
# We configure the destinations to automatically prove all
# packets adressed to it. By doing this, RNS will automatically
# packets addressed to it. By doing this, RNS will automatically
# generate a proof for each incoming packet and transmit it
# back to the sender of that packet. This will let anyone that
# tries to communicate with the destination know whether their
+16 -4
View File
@@ -5,6 +5,7 @@
# of the packet. #
##########################################################
import os
import argparse
import RNS
@@ -27,8 +28,19 @@ def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our echo server
server_identity = RNS.Identity()
# Load identity from file if it exist or randomley create
if configpath:
ifilepath = "%s/storage/identitiesy/%s" % (configpath,APP_NAME)
else:
ifilepath = "%s/storage/identities/%s" % (RNS.Reticulum.configdir,APP_NAME)
if os.path.exists(ifilepath):
# Load identity from file
server_identity = RNS.Identity.from_file(ifilepath)
RNS.log("loaded identity from file: "+ifilepath, RNS.LOG_VERBOSE)
else:
# Randomly create a new identity for our echo example
server_identity = RNS.Identity()
RNS.log("created new identity", RNS.LOG_VERBOSE)
# We create a destination that clients can query. We want
# to be able to verify echo replies to our clients, so we
@@ -46,7 +58,7 @@ def server(configpath):
)
# We configure the destination to automatically prove all
# packets adressed to it. By doing this, RNS will automatically
# packets addressed to it. By doing this, RNS will automatically
# generate a proof for each incoming packet and transmit it
# back to the sender of that packet.
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
@@ -328,4 +340,4 @@ if __name__ == "__main__":
client(args.destination, configarg, timeout=timeoutarg)
except KeyboardInterrupt:
print("")
exit()
exit()
+1 -2
View File
@@ -449,8 +449,7 @@ def link_established(link):
# And set up a small job to check for
# a potential timeout in receiving the
# file list
thread = threading.Thread(target=filelist_timeout_job)
thread.setDaemon(True)
thread = threading.Thread(target=filelist_timeout_job, daemon=True)
thread.start()
# This job just sleeps for the specified
+15 -3
View File
@@ -28,8 +28,20 @@ def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
# Load identity from file if it exist or randomley create
if configpath:
ifilepath = "%s/storage/identitiesy/%s" % (configpath,APP_NAME)
else:
ifilepath = "%s/storage/identities/%s" % (RNS.Reticulum.configdir,APP_NAME)
RNS.log("ifilepath: %s" % ifilepath)
if os.path.exists(ifilepath):
# Load identity from file
server_identity = RNS.Identity.from_file(ifilepath)
RNS.log("loaded identity from file: "+ifilepath, RNS.LOG_VERBOSE)
else:
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
RNS.log("created new identity", RNS.LOG_VERBOSE)
# We create a destination that clients can connect to. We
# want clients to create links to this destination, so we
@@ -288,4 +300,4 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("")
exit()
exit()
+2 -2
View File
@@ -25,7 +25,7 @@ def program_setup(configpath):
# Destinations are endpoints in Reticulum, that can be addressed
# and communicated with. Destinations can also announce their
# existence, which will let the network know they are reachable
# and autoomatically create paths to them, from anywhere else
# and automatically create paths to them, from anywhere else
# in the network.
destination = RNS.Destination(
identity,
@@ -36,7 +36,7 @@ def program_setup(configpath):
)
# We configure the destination to automatically prove all
# packets adressed to it. By doing this, RNS will automatically
# packets addressed to it. By doing this, RNS will automatically
# generate a proof for each incoming packet and transmit it
# back to the sender of that packet. This will let anyone that
# tries to communicate with the destination know whether their
+2
View File
@@ -0,0 +1,2 @@
ko_fi: markqvist
custom: "https://unsigned.io/donate"
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License, unless otherwise noted
Copyright (c) 2016-2022 Mark Qvist / unsigned.io
Copyright (c) 2016-2024 Mark Qvist / unsigned.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+7 -7
View File
@@ -45,7 +45,7 @@ For more info, see [reticulum.network](https://reticulum.network/)
- Initiator anonymity, communicate without revealing your identity
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
- Forward Secrecy with ephemeral Elliptic Curve Diffie-Hellman keys on Curve25519
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for on-the-wire / over-the-air encryption
- Reticulum uses the following format for encrypted tokens:
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
- AES-128 in CBC mode with PKCS7 padding
- HMAC using SHA256 for authentication
@@ -80,7 +80,7 @@ following resources.
## Where can Reticulum be used?
Over practically any medium that can support at least a half-duplex channel
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
WiFi and Ethernet devices, free-space optical links, and similar systems are
all examples of the types of physical devices Reticulum can use.
@@ -191,8 +191,8 @@ functionality and performance on low-bandwidth mediums. The goal is to
provide a dynamic performance envelope from 250 bits per second, to 1 gigabit
per second on normal hardware.
Currently, the usable performance envelope is approximately 500 bits per second
to 20 megabits per second, with physical mediums faster than that not being
Currently, the usable performance envelope is approximately 150 bits per second
to 40 megabits per second, with physical mediums faster than that not being
saturated. Performance beyond the current level is intended for future
upgrades, but not highly prioritised at this point in time.
@@ -273,16 +273,16 @@ The testnet also contains a number of [Nomad Network](https://github.com/markqvi
You can help support the continued development of open, free and private communications systems by donating via one of the following channels:
- Monero:
```
```
84FpY1QbxHcgdseePYNmhTHcrgMX4nFfBYtz2GKYToqHVVhJp8Eaw1Z1EedRnKD19b3B8NiLCGVxzKV17UMmmeEsCrPyA5w
```
- Ethereum
```
0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a
0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73
```
- Bitcoin
```
3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq
35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH
```
- Ko-Fi: https://ko-fi.com/markqvist
+22
View File
@@ -1,3 +1,25 @@
# MIT License
#
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import annotations
import bz2
import sys
+6 -6
View File
@@ -45,10 +45,10 @@ class Fernet():
def __init__(self, key = None):
if key == None:
raise ValueError("Fernet key cannot be None")
raise ValueError("Token key cannot be None")
if len(key) != 32:
raise ValueError("Fernet key must be 32 bytes, not "+str(len(key)))
raise ValueError("Token key must be 32 bytes, not "+str(len(key)))
self._signing_key = key[:16]
self._encryption_key = key[16:]
@@ -72,7 +72,7 @@ class Fernet():
current_time = int(time.time())
if not isinstance(data, bytes):
raise TypeError("Fernet token plaintext input must be bytes")
raise TypeError("Token plaintext input must be bytes")
ciphertext = AES_128_CBC.encrypt(
plaintext = PKCS7.pad(data),
@@ -87,10 +87,10 @@ class Fernet():
def decrypt(self, token = None):
if not isinstance(token, bytes):
raise TypeError("Fernet token must be bytes")
raise TypeError("Token must be bytes")
if not self.verify_hmac(token):
raise ValueError("Fernet token HMAC was invalid")
raise ValueError("Token HMAC was invalid")
iv = token[:16]
ciphertext = token[16:-32]
@@ -107,4 +107,4 @@ class Fernet():
return plaintext
except Exception as e:
raise ValueError("Could not decrypt Fernet token")
raise ValueError("Could not decrypt token")
+1 -1
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
+15 -6
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -145,9 +145,12 @@ class Identity:
except:
pass
for destination_hash in storage_known_destinations:
if not destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
try:
for destination_hash in storage_known_destinations:
if not destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
except Exception as e:
RNS.log("Skipped recombining known destinations from disk, since an error occurred: "+str(e), RNS.LOG_WARNING)
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_DEBUG)
file = open(RNS.Reticulum.storagepath+"/known_destinations","wb")
@@ -164,6 +167,7 @@ class Identity:
except Exception as e:
RNS.log("Error while saving known destinations to disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.trace_exception(e)
Identity.saving_known_destinations = False
@@ -181,7 +185,8 @@ class Identity:
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destination from storage", RNS.LOG_VERBOSE)
except:
except Exception as e:
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
else:
RNS.log("Destinations file does not exist, no known destinations loaded", RNS.LOG_VERBOSE)
@@ -217,7 +222,7 @@ class Identity:
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
@staticmethod
def validate_announce(packet):
def validate_announce(packet, only_validate_signature=False):
try:
if packet.packet_type == RNS.Packet.ANNOUNCE:
destination_hash = packet.destination_hash
@@ -238,6 +243,10 @@ class Identity:
announced_identity.load_public_key(public_key)
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
if only_validate_signature:
del announced_identity
return True
hash_material = name_hash+announced_identity.hash
expected_hash = RNS.Identity.full_hash(hash_material)[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
+4 -2
View File
@@ -77,8 +77,7 @@ class AX25KISSInterface(Interface):
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
RNS.panic()
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 564
@@ -367,5 +366,8 @@ class AX25KISSInterface(Interface):
RNS.log("Reconnected serial port for "+str(self))
def should_ingress_limit(self):
return False
def __str__(self):
return "AX25KISSInterface["+self.name+"]"
+4 -2
View File
@@ -82,8 +82,7 @@ class KISSInterface(Interface):
else:
raise SystemError("Android-specific interface was used on non-Android OS")
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 564
@@ -403,5 +402,8 @@ class KISSInterface(Interface):
RNS.log("Reconnected serial port for "+str(self))
def should_ingress_limit(self):
return False
def __str__(self):
return "KISSInterface["+self.name+"]"
+29 -9
View File
@@ -59,6 +59,7 @@ class KISS():
CMD_FB_EXT = 0x41
CMD_FB_READ = 0x42
CMD_FB_WRITE = 0x43
CMD_BT_CTRL = 0x46
CMD_PLATFORM = 0x48
CMD_MCU = 0x49
CMD_FW_VERSION = 0x50
@@ -114,7 +115,7 @@ class AndroidBluetoothManager():
if self.bt_enabled():
return self.bt_adapter.getDefaultAdapter().getBondedDevices()
else:
RNS.log("Could not query paired devices, Bluetooth is disabled", RNS.LOG_DEBUG)
RNS.log("Could not query paired devices, Bluetooth is disabled", RNS.LOG_EXTREME)
return []
def get_potential_devices(self):
@@ -166,8 +167,8 @@ class AndroidBluetoothManager():
raise IOError("The Bluetooth RFcomm socket could not be connected: "+str(e))
except Exception as e:
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_EXTREME)
RNS.log("The contained exception was: "+str(e), RNS.LOG_EXTREME)
def close(self):
if self.connected:
@@ -232,6 +233,10 @@ class RNodeInterface(Interface):
RECONNECT_WAIT = 5
PORT_IO_TIMEOUT = 3
Q_SNR_MIN_BASE = -9
Q_SNR_MAX = 6
Q_SNR_STEP = 2
@classmethod
def bluetooth_control(device_serial = None, port = None, enable_bluetooth = False, disable_bluetooth = False, pairing_mode = False):
if (port != None or device_serial != None) and (enable_bluetooth or disable_bluetooth or pairing_mode):
@@ -349,8 +354,7 @@ class RNodeInterface(Interface):
else:
raise SystemError("Android-specific interface was used on non-Android OS")
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 508
@@ -598,8 +602,11 @@ class RNodeInterface(Interface):
raise IOError("Invalid device firmware")
if self.serial != None and self.port != None:
self.timeout = 200
RNS.log("Serial port "+self.port+" is now open")
if self.bt_manager != None and self.bt_manager.connected:
self.timeout = 1500
RNS.log("Bluetooth connection to RNode now open")
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
@@ -861,9 +868,6 @@ class RNodeInterface(Interface):
self.owner.inbound(data, self)
threading.Thread(target=af, daemon=True).start()
self.r_stat_rssi = None
self.r_stat_snr = None
def processOutgoing(self,data):
datalen = len(data)
@@ -1045,6 +1049,19 @@ class RNodeInterface(Interface):
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
try:
sfs = self.r_sf-7
snr = self.r_stat_snr
q_snr_min = RNodeInterface.Q_SNR_MIN_BASE-sfs*RNodeInterface.Q_SNR_STEP
q_snr_max = RNodeInterface.Q_SNR_MAX
q_snr_span = q_snr_max-q_snr_min
quality = round(((snr-q_snr_min)/(q_snr_span))*100,1)
if quality > 100.0: quality = 100.0
if quality < 0.0: quality = 0.0
self.r_stat_q = quality
except:
pass
elif (command == KISS.CMD_ST_ALOCK):
if (byte == KISS.FESC):
escape = True
@@ -1156,7 +1173,7 @@ class RNodeInterface(Interface):
if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout", RNS.LOG_DEBUG)
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
data_buffer = b""
in_frame = False
command = KISS.CMD_UNKNOWN
@@ -1234,6 +1251,9 @@ class RNodeInterface(Interface):
self.setRadioState(KISS.RADIO_STATE_OFF)
self.leave()
def should_ingress_limit(self):
return False
def __str__(self):
return "RNodeInterface["+str(self.name)+"]"
+4 -2
View File
@@ -72,8 +72,7 @@ class SerialInterface(Interface):
else:
raise SystemError("Android-specific interface was used on non-Android OS")
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 564
@@ -254,5 +253,8 @@ class SerialInterface(Interface):
RNS.log("Reconnected serial port for "+str(self))
def should_ingress_limit(self):
return False
def __str__(self):
return "SerialInterface["+self.name+"]"
+27 -5
View File
@@ -43,6 +43,9 @@ class AutoInterface(Interface):
SCOPE_ORGANISATION = "8"
SCOPE_GLOBAL = "e"
MULTICAST_PERMANENT_ADDRESS_TYPE = "0"
MULTICAST_TEMPORARY_ADDRESS_TYPE = "1"
PEERING_TIMEOUT = 7.5
ALL_IGNORE_IFS = ["lo0"]
@@ -74,13 +77,11 @@ class AutoInterface(Interface):
ifas = self.netinfo.ifaddresses(ifname)
return ifas
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, multicast_address_type=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
from RNS.vendor.ifaddr import niwrapper
super().__init__()
self.netinfo = niwrapper
self.rxb = 0
self.txb = 0
self.HW_MTU = 1064
self.IN = True
@@ -105,6 +106,11 @@ class AutoInterface(Interface):
self.peering_timeout = AutoInterface.PEERING_TIMEOUT
self.multicast_echo_timeout = AutoInterface.PEERING_TIMEOUT/2
# Increase peering timeout on Android, due to potential
# low-power modes implemented on many chipsets.
if RNS.vendor.platformutils.is_android():
self.peering_timeout *= 3
if allowed_interfaces == None:
self.allowed_interfaces = []
else:
@@ -125,6 +131,15 @@ class AutoInterface(Interface):
else:
self.discovery_port = discovery_port
if multicast_address_type == None:
self.multicast_address_type = AutoInterface.MULTICAST_TEMPORARY_ADDRESS_TYPE
elif str(multicast_address_type).lower() == "temporary":
self.multicast_address_type = AutoInterface.MULTICAST_TEMPORARY_ADDRESS_TYPE
elif str(multicast_address_type).lower() == "permanent":
self.multicast_address_type = AutoInterface.MULTICAST_PERMANENT_ADDRESS_TYPE
else:
self.multicast_address_type = AutoInterface.MULTICAST_TEMPORARY_ADDRESS_TYPE
if data_port == None:
self.data_port = AutoInterface.DEFAULT_DATA_PORT
else:
@@ -153,7 +168,7 @@ class AutoInterface(Interface):
gt += ":"+"{:02x}".format(g[9]+(g[8]<<8))
gt += ":"+"{:02x}".format(g[11]+(g[10]<<8))
gt += ":"+"{:02x}".format(g[13]+(g[12]<<8))
self.mcast_discovery_address = "ff1"+self.discovery_scope+":"+gt
self.mcast_discovery_address = "ff"+self.multicast_address_type+self.discovery_scope+":"+gt
suitable_interfaces = 0
for ifname in self.list_interfaces():
@@ -331,6 +346,8 @@ class AutoInterface(Interface):
thread.daemon = True
thread.start()
self.carrier_changed = True
except Exception as e:
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -428,6 +445,11 @@ class AutoInterface(Interface):
self.txb += len(data)
# Until per-device sub-interfacing is implemented,
# ingress limiting should be disabled on AutoInterface
def should_ingress_limit(self):
return False
def __str__(self):
return "AutoInterface["+self.name+"]"
+8 -4
View File
@@ -390,8 +390,7 @@ class I2PInterfacePeer(Interface):
TUNNEL_STATE_STALE = 0x02
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 1064
@@ -832,8 +831,7 @@ class I2PInterface(Interface):
BITRATE_GUESS = 256*1000
def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 1064
@@ -964,6 +962,12 @@ class I2PInterface(Interface):
def processOutgoing(self, data):
pass
def received_announce(self, from_spawned=False):
if from_spawned: self.ia_freq_deque.append(time.time())
def sent_announce(self, from_spawned=False):
if from_spawned: self.oa_freq_deque.append(time.time())
def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
self.i2p.stop()
+148 -3
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,7 @@
import RNS
import time
import threading
from collections import deque
class Interface:
IN = False
@@ -39,18 +40,161 @@ class Interface:
MODE_BOUNDARY = 0x05
MODE_GATEWAY = 0x06
# Which interface modes a Transport Node
# should actively discover paths for.
# Which interface modes a Transport Node should
# actively discover paths for.
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
# How many samples to use for announce
# frequency calculations
IA_FREQ_SAMPLES = 6
OA_FREQ_SAMPLES = 6
# Maximum amount of ingress limited announces
# to hold at any given time.
MAX_HELD_ANNOUNCES = 256
# How long a spawned interface will be
# considered to be newly created. Two
# hours by default.
IC_NEW_TIME = 2*60*60
IC_BURST_FREQ_NEW = 3.5
IC_BURST_FREQ = 12
IC_BURST_HOLD = 1*60
IC_BURST_PENALTY = 5*60
IC_HELD_RELEASE_INTERVAL = 30
def __init__(self):
self.rxb = 0
self.txb = 0
self.created = time.time()
self.online = False
self.bitrate = 1e6
self.ingress_control = True
self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES
self.ic_burst_hold = Interface.IC_BURST_HOLD
self.ic_burst_active = False
self.ic_burst_activated = 0
self.ic_held_release = 0
self.ic_burst_freq_new = Interface.IC_BURST_FREQ_NEW
self.ic_burst_freq = Interface.IC_BURST_FREQ
self.ic_new_time = Interface.IC_NEW_TIME
self.ic_burst_penalty = Interface.IC_BURST_PENALTY
self.ic_held_release_interval = Interface.IC_HELD_RELEASE_INTERVAL
self.held_announces = {}
self.ia_freq_deque = deque(maxlen=Interface.IA_FREQ_SAMPLES)
self.oa_freq_deque = deque(maxlen=Interface.OA_FREQ_SAMPLES)
def get_hash(self):
return RNS.Identity.full_hash(str(self).encode("utf-8"))
# This is a generic function for determining when an interface
# should activate ingress limiting. Since this can vary for
# different interface types, this function should be overwritten
# in case a particular interface requires a different approach.
def should_ingress_limit(self):
if self.ingress_control:
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
ia_freq = self.incoming_announce_frequency()
if self.ic_burst_active:
if ia_freq < freq_threshold and time.time() > self.ic_burst_activated+self.ic_burst_hold:
self.ic_burst_active = False
self.ic_held_release = time.time() + self.ic_burst_penalty
return True
else:
if ia_freq > freq_threshold:
self.ic_burst_active = True
self.ic_burst_activated = time.time()
return True
else:
return False
else:
return False
def age(self):
return time.time()-self.created
def hold_announce(self, announce_packet):
if announce_packet.destination_hash in self.held_announces:
self.held_announces[announce_packet.destination_hash] = announce_packet
elif not len(self.held_announces) >= self.ic_max_held_announces:
self.held_announces[announce_packet.destination_hash] = announce_packet
def process_held_announces(self):
try:
if not self.should_ingress_limit() and len(self.held_announces) > 0 and time.time() > self.ic_held_release:
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
ia_freq = self.incoming_announce_frequency()
if ia_freq < freq_threshold:
selected_announce_packet = None
min_hops = RNS.Transport.PATHFINDER_M
for destination_hash in self.held_announces:
announce_packet = self.held_announces[destination_hash]
if announce_packet.hops < min_hops:
min_hops = announce_packet.hops
selected_announce_packet = announce_packet
if selected_announce_packet != None:
RNS.log("Releasing held announce packet "+str(selected_announce_packet)+" from "+str(self), RNS.LOG_EXTREME)
self.ic_held_release = time.time() + self.ic_held_release_interval
self.held_announces.pop(selected_announce_packet.destination_hash)
def release():
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
threading.Thread(target=release, daemon=True).start()
except Exception as e:
RNS.log("An error occurred while processing held announces for "+str(self), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
def received_announce(self):
self.ia_freq_deque.append(time.time())
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.received_announce(from_spawned=True)
def sent_announce(self):
self.oa_freq_deque.append(time.time())
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.sent_announce(from_spawned=True)
def incoming_announce_frequency(self):
if not len(self.ia_freq_deque) > 1:
return 0
else:
dq_len = len(self.ia_freq_deque)
delta_sum = 0
for i in range(1,dq_len):
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
if delta_sum == 0:
avg = 0
else:
avg = 1/(delta_sum/(dq_len))
return avg
def outgoing_announce_frequency(self):
if not len(self.oa_freq_deque) > 1:
return 0
else:
dq_len = len(self.oa_freq_deque)
delta_sum = 0
for i in range(1,dq_len):
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
if delta_sum == 0:
avg = 0
else:
avg = 1/(delta_sum/(dq_len))
return avg
def process_announce_queue(self):
if not hasattr(self, "announce_cap"):
self.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
@@ -79,6 +223,7 @@ class Interface:
self.announce_allowed_at = now + wait_time
self.processOutgoing(selected["raw"])
self.sent_announce()
if selected in self.announce_queue:
self.announce_queue.remove(selected)
+4 -2
View File
@@ -70,8 +70,7 @@ class KISSInterface(Interface):
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
RNS.panic()
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 564
@@ -349,5 +348,8 @@ class KISSInterface(Interface):
RNS.log("Reconnected serial port for "+str(self))
def should_ingress_limit(self):
return False
def __str__(self):
return "KISSInterface["+self.name+"]"
+34 -13
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +28,7 @@ import time
import sys
import os
import RNS
from threading import Lock
class HDLC():
FLAG = 0x7E
@@ -50,11 +51,10 @@ class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
self.server_address = self.socket.getsockname()
class LocalClientInterface(Interface):
RECONNECT_WAIT = 3
RECONNECT_WAIT = 8
def __init__(self, owner, name, target_port = None, connected_socket=None):
self.rxb = 0
self.txb = 0
super().__init__()
# TODO: Remove at some point
# self.rxptime = 0
@@ -78,6 +78,7 @@ class LocalClientInterface(Interface):
self.target_ip = None
self.target_port = None
self.socket = connected_socket
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.is_connected_to_shared_instance = False
@@ -103,8 +104,12 @@ class LocalClientInterface(Interface):
thread.daemon = True
thread.start()
def should_ingress_limit(self):
return False
def connect(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.socket.connect((self.target_ip, self.target_port))
self.online = True
@@ -137,7 +142,10 @@ class LocalClientInterface(Interface):
thread = threading.Thread(target=self.read_loop)
thread.daemon = True
thread.start()
RNS.Transport.shared_connection_reappeared()
def job():
time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
RNS.Transport.shared_connection_reappeared()
threading.Thread(target=job, daemon=True).start()
else:
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
@@ -145,9 +153,6 @@ class LocalClientInterface(Interface):
def processIncoming(self, data):
if self._force_bitrate:
time.sleep(len(data) / self.bitrate * 8)
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data)
@@ -165,8 +170,16 @@ class LocalClientInterface(Interface):
if self.online:
try:
self.writing = True
if self._force_bitrate:
time.sleep(len(data) / self.bitrate * 8)
if not hasattr(self, "send_lock"):
self.send_lock = Lock()
with self.send_lock:
s = len(data) / self.bitrate * 8
RNS.log(f"Simulating latency of {RNS.prettytime(s)} for {len(data)} bytes")
time.sleep(s)
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
self.socket.sendall(data)
self.writing = False
@@ -259,7 +272,8 @@ class LocalClientInterface(Interface):
RNS.Transport.local_client_interfaces.remove(self)
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.clients -= 1
RNS.Transport.owner._should_persist_data()
if hasattr(RNS.Transport, "owner") and RNS.Transport.owner != None:
RNS.Transport.owner._should_persist_data()
if nowarning == False:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
@@ -280,8 +294,7 @@ class LocalClientInterface(Interface):
class LocalServerInterface(Interface):
def __init__(self, owner, bindport=None):
self.rxb = 0
self.txb = 0
super().__init__()
self.online = False
self.clients = 0
@@ -329,7 +342,9 @@ class LocalServerInterface(Interface):
spawned_interface.target_port = str(handler.client_address[1])
spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate
RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_EXTREME)
if hasattr(self, "_force_bitrate"):
spawned_interface._force_bitrate = self._force_bitrate
# RNS.log("Accepting new connection to shared instance: "+str(spawned_interface), RNS.LOG_EXTREME)
RNS.Transport.interfaces.append(spawned_interface)
RNS.Transport.local_client_interfaces.append(spawned_interface)
self.clients += 1
@@ -338,6 +353,12 @@ class LocalServerInterface(Interface):
def processOutgoing(self, data):
pass
def received_announce(self, from_spawned=False):
if from_spawned: self.ia_freq_deque.append(time.time())
def sent_announce(self, from_spawned=False):
if from_spawned: self.oa_freq_deque.append(time.time())
def __str__(self):
return "Shared Instance["+str(self.bind_port)+"]"
+1 -2
View File
@@ -54,8 +54,7 @@ class PipeInterface(Interface):
if respawn_delay == None:
respawn_delay = 5
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 1064
+28 -9
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -59,6 +59,7 @@ class KISS():
CMD_FB_EXT = 0x41
CMD_FB_READ = 0x42
CMD_FB_WRITE = 0x43
CMD_BT_CTRL = 0x46
CMD_PLATFORM = 0x48
CMD_MCU = 0x49
CMD_FW_VERSION = 0x50
@@ -91,7 +92,7 @@ class RNodeInterface(Interface):
MAX_CHUNK = 32768
FREQ_MIN = 137000000
FREQ_MAX = 1020000000
FREQ_MAX = 3000000000
RSSI_OFFSET = 157
@@ -102,9 +103,13 @@ class RNodeInterface(Interface):
RECONNECT_WAIT = 5
Q_SNR_MIN_BASE = -9
Q_SNR_MAX = 6
Q_SNR_STEP = 2
def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None, st_alock = None, lt_alock = None):
if RNS.vendor.platformutils.is_android():
raise SystemError("Invlaid interface type. The Android-specific RNode interface must be used on Android")
raise SystemError("Invalid interface type. The Android-specific RNode interface must be used on Android")
import importlib
if importlib.util.find_spec('serial') != None:
@@ -114,8 +119,7 @@ class RNodeInterface(Interface):
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
RNS.panic()
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 508
@@ -186,15 +190,15 @@ class RNodeInterface(Interface):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.txpower < 0 or self.txpower > 17):
if (self.txpower < 0 or self.txpower > 22):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 500000):
if (self.bandwidth < 7800 or self.bandwidth > 1625000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
if (self.sf < 7 or self.sf > 12):
if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
self.validcfg = False
@@ -672,6 +676,18 @@ class RNodeInterface(Interface):
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
try:
sfs = self.r_sf-7
snr = self.r_stat_snr
q_snr_min = RNodeInterface.Q_SNR_MIN_BASE-sfs*RNodeInterface.Q_SNR_STEP
q_snr_max = RNodeInterface.Q_SNR_MAX
q_snr_span = q_snr_max-q_snr_min
quality = round(((snr-q_snr_min)/(q_snr_span))*100,1)
if quality > 100.0: quality = 100.0
if quality < 0.0: quality = 0.0
self.r_stat_q = quality
except:
pass
elif (command == KISS.CMD_ST_ALOCK):
if (byte == KISS.FESC):
escape = True
@@ -783,7 +799,7 @@ class RNodeInterface(Interface):
else:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout", RNS.LOG_DEBUG)
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
data_buffer = b""
in_frame = False
command = KISS.CMD_UNKNOWN
@@ -838,5 +854,8 @@ class RNodeInterface(Interface):
self.setRadioState(KISS.RADIO_STATE_OFF)
self.leave()
def should_ingress_limit(self):
return False
def __str__(self):
return "RNodeInterface["+str(self.name)+"]"
+4 -2
View File
@@ -60,8 +60,7 @@ class SerialInterface(Interface):
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
RNS.panic()
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 564
@@ -201,5 +200,8 @@ class SerialInterface(Interface):
RNS.log("Reconnected serial port for "+str(self))
def should_ingress_limit(self):
return False
def __str__(self):
return "SerialInterface["+self.name+"]"
+11 -4
View File
@@ -79,8 +79,7 @@ class TCPClientInterface(Interface):
I2P_PROBES = 5
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 1064
@@ -117,6 +116,8 @@ class TCPClientInterface(Interface):
elif platform.system() == "Darwin":
self.set_timeouts_osx()
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
elif target_ip != None and target_port != None:
self.receives = True
self.target_ip = target_ip
@@ -201,6 +202,7 @@ class TCPClientInterface(Interface):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.socket.connect((self.target_ip, self.target_port))
self.socket.settimeout(None)
self.online = True
@@ -419,8 +421,7 @@ class TCPServerInterface(Interface):
return ifaddr[netinfo.AF_INET][0]["broadcast"]
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 1064
@@ -505,6 +506,12 @@ class TCPServerInterface(Interface):
self.clients += 1
spawned_interface.read_loop()
def received_announce(self, from_spawned=False):
if from_spawned: self.ia_freq_deque.append(time.time())
def sent_announce(self, from_spawned=False):
if from_spawned: self.oa_freq_deque.append(time.time())
def processOutgoing(self, data):
pass
+1 -2
View File
@@ -45,8 +45,7 @@ class UDPInterface(Interface):
return ifaddr[netinfo.AF_INET][0]["broadcast"]
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
self.rxb = 0
self.txb = 0
super().__init__()
self.HW_MTU = 1064
+230 -133
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -112,9 +112,10 @@ class Link:
link = Link(owner = owner, peer_pub_bytes=data[:Link.ECPUBSIZE//2], peer_sig_pub_bytes=data[Link.ECPUBSIZE//2:Link.ECPUBSIZE])
link.set_link_id(packet)
link.destination = packet.destination
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops)
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE
link.establishment_cost += len(packet.raw)
RNS.log("Validating link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE)
RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_EXTREME)
link.handshake()
link.attached_interface = packet.receiving_interface
link.prove()
@@ -123,7 +124,7 @@ class Link:
link.last_inbound = time.time()
link.start_watchdog()
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_VERBOSE)
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_DEBUG)
return link
except Exception as e:
@@ -132,7 +133,7 @@ class Link:
return None
else:
RNS.log("Invalid link request payload size, dropping request", RNS.LOG_VERBOSE)
RNS.log("Invalid link request payload size, dropping request", RNS.LOG_DEBUG)
return None
@@ -149,10 +150,14 @@ class Link:
self.last_inbound = 0
self.last_outbound = 0
self.last_proof = 0
self.last_data = 0
self.tx = 0
self.rx = 0
self.txbytes = 0
self.rxbytes = 0
self.rssi = None
self.snr = None
self.q = None
self.traffic_timeout_factor = Link.TRAFFIC_TIMEOUT_FACTOR
self.keepalive_timeout_factor = Link.KEEPALIVE_TIMEOUT_FACTOR
self.keepalive = Link.KEEPALIVE
@@ -163,16 +168,21 @@ class Link:
self.type = RNS.Destination.LINK
self.owner = owner
self.destination = destination
self.expected_hops = None
self.attached_interface = None
self.__remote_identity = None
self.__track_phy_stats = False
self._channel = None
if self.destination == None:
self.initiator = False
self.prv = X25519PrivateKey.generate()
self.sig_prv = self.owner.identity.sig_prv
else:
self.initiator = True
self.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, RNS.Transport.hops_to(destination.hash))
self.expected_hops = RNS.Transport.hops_to(self.destination.hash)
self.establishment_timeout = RNS.Reticulum.get_instance().get_first_hop_timeout(destination.hash)
self.establishment_timeout += Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, RNS.Transport.hops_to(destination.hash))
self.prv = X25519PrivateKey.generate()
self.sig_prv = Ed25519PrivateKey.generate()
@@ -208,6 +218,7 @@ class Link:
self.packet.send()
self.had_outbound()
RNS.log("Link request "+RNS.prettyhexrep(self.link_id)+" sent to "+str(self.destination), RNS.LOG_DEBUG)
RNS.log(f"Establishment timeout is {RNS.prettytime(self.establishment_timeout)} for link request "+RNS.prettyhexrep(self.link_id), RNS.LOG_EXTREME)
def load_peer(self, peer_pub_bytes, peer_sig_pub_bytes):
@@ -319,7 +330,7 @@ class Link:
:param identity: An RNS.Identity instance to identify as.
"""
if self.initiator:
if self.initiator and self.status == Link.ACTIVE:
signed_data = self.link_id + identity.get_public_key()
signature = identity.sign(signed_data)
proof_data = identity.get_public_key() + signature
@@ -345,7 +356,7 @@ class Link:
packed_request = umsgpack.packb(unpacked_request)
if timeout == None:
timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME/4.0
timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME*1.125
if len(packed_request) <= Link.MDU:
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
@@ -385,24 +396,57 @@ class Link:
try:
measured_rtt = time.time() - self.request_time
plaintext = self.decrypt(packet.data)
rtt = umsgpack.unpackb(plaintext)
self.rtt = max(measured_rtt, rtt)
self.status = Link.ACTIVE
self.activated_at = time.time()
if plaintext != None:
rtt = umsgpack.unpackb(plaintext)
self.rtt = max(measured_rtt, rtt)
self.status = Link.ACTIVE
self.activated_at = time.time()
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
self.establishment_rate = self.establishment_cost/self.rtt
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
self.establishment_rate = self.establishment_cost/self.rtt
try:
if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self)
except Exception as e:
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
try:
if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self)
except Exception as e:
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e:
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
self.teardown()
def track_phy_stats(self, track):
"""
You can enable physical layer statistics on a per-link basis. If this is enabled,
and the link is running over an interface that supports reporting physical layer
statistics, you will be able to retrieve stats such as *RSSI*, *SNR* and physical
*Link Quality* for the link.
:param track: Whether or not to keep track of physical layer statistics. Value must be ``True`` or ``False``.
"""
if track:
self.__track_phy_stats = True
else:
self.__track_phy_stats = False
def get_rssi(self):
"""
:returns: The physical layer *Received Signal Strength Indication* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
"""
return self.rssi
def get_snr(self):
"""
:returns: The physical layer *Signal-to-Noise Ratio* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
"""
return self.rssi
def get_q(self):
"""
:returns: The physical layer *Link Quality* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
"""
return self.rssi
def get_establishment_rate(self):
"""
:returns: The data transfer rate at which the link establishment procedure ocurred, in bits per second.
@@ -420,7 +464,7 @@ class Link:
def no_inbound_for(self):
"""
:returns: The time in seconds since last inbound packet on the link.
:returns: The time in seconds since last inbound packet on the link. This includes keepalive packets.
"""
activated_at = self.activated_at if self.activated_at != None else 0
last_inbound = max(self.last_inbound, activated_at)
@@ -428,13 +472,19 @@ class Link:
def no_outbound_for(self):
"""
:returns: The time in seconds since last outbound packet on the link.
:returns: The time in seconds since last outbound packet on the link. This includes keepalive packets.
"""
return time.time() - self.last_outbound
def no_data_for(self):
"""
:returns: The time in seconds since payload data traversed the link. This excludes keepalive packets.
"""
return time.time() - self.last_data
def inactive_for(self):
"""
:returns: The time in seconds since activity on the link.
:returns: The time in seconds since activity on the link. This includes keepalive packets.
"""
return min(self.no_inbound_for(), self.no_outbound_for())
@@ -444,8 +494,10 @@ class Link:
"""
return self.__remote_identity
def had_outbound(self):
def had_outbound(self, is_keepalive=False):
self.last_outbound = time.time()
if not is_keepalive:
self.last_data = self.last_outbound
def teardown(self):
"""
@@ -472,6 +524,7 @@ class Link:
self.teardown_reason = Link.DESTINATION_CLOSED
else:
self.teardown_reason = Link.INITIATOR_CLOSED
self.__update_phy_stats(packet)
self.link_closed()
except Exception as e:
pass
@@ -577,10 +630,25 @@ class Link:
sleep(sleep_time)
def __update_phy_stats(self, packet, query_shared = True):
if self.__track_phy_stats:
if query_shared:
reticulum = RNS.Reticulum.get_instance()
if packet.rssi == None: packet.rssi = reticulum.get_packet_rssi(packet.packet_hash)
if packet.snr == None: packet.snr = reticulum.get_packet_snr(packet.packet_hash)
if packet.q == None: packet.q = reticulum.get_packet_q(packet.packet_hash)
if packet.rssi != None:
self.rssi = packet.rssi
if packet.snr != None:
self.snr = packet.snr
if packet.q != None:
self.q = packet.q
def send_keepalive(self):
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
keepalive_packet.send()
self.had_outbound()
self.had_outbound(is_keepalive = True)
def handle_request(self, request_id, unpacked_request):
if self.status == Link.ACTIVE:
@@ -631,7 +699,9 @@ class Link:
remove = pending_request
try:
pending_request.response_size = response_size
pending_request.response_transfer_size = response_transfer_size
if pending_request.response_transfer_size == None:
pending_request.response_transfer_size = 0
pending_request.response_transfer_size += response_transfer_size
pending_request.response_received(response_data)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -684,135 +754,167 @@ class Link:
RNS.log("Link-associated packet received on unexpected interface! Someone might be trying to manipulate your communication!", RNS.LOG_ERROR)
else:
self.last_inbound = time.time()
if packet.context != RNS.Packet.KEEPALIVE:
self.last_data = self.last_inbound
self.rx += 1
self.rxbytes += len(packet.data)
if self.status == Link.STALE:
self.status = Link.ACTIVE
if packet.packet_type == RNS.Packet.DATA:
should_query = False
if packet.context == RNS.Packet.NONE:
plaintext = self.decrypt(packet.data)
if self.callbacks.packet != None:
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
thread.daemon = True
thread.start()
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
packet.prove()
if plaintext != None:
if self.callbacks.packet != None:
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
thread.daemon = True
thread.start()
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
packet.prove()
should_query = True
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
if self.destination.callbacks.proof_requested:
try:
self.destination.callbacks.proof_requested(packet)
except Exception as e:
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
if self.destination.callbacks.proof_requested:
try:
if self.destination.callbacks.proof_requested(packet):
packet.prove()
should_query = True
except Exception as e:
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
self.__update_phy_stats(packet, query_shared=should_query)
elif packet.context == RNS.Packet.LINKIDENTIFY:
plaintext = self.decrypt(packet.data)
if plaintext != None:
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
signed_data = self.link_id+public_key
signature = plaintext[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Identity.SIGLENGTH//8]
identity = RNS.Identity(create_keys=False)
identity.load_public_key(public_key)
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
signed_data = self.link_id+public_key
signature = plaintext[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Identity.SIGLENGTH//8]
identity = RNS.Identity(create_keys=False)
identity.load_public_key(public_key)
if identity.validate(signature, signed_data):
self.__remote_identity = identity
if self.callbacks.remote_identified != None:
try:
self.callbacks.remote_identified(self, self.__remote_identity)
except Exception as e:
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
if identity.validate(signature, signed_data):
self.__remote_identity = identity
if self.callbacks.remote_identified != None:
try:
self.callbacks.remote_identified(self, self.__remote_identity)
except Exception as e:
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
self.__update_phy_stats(packet, query_shared=True)
elif packet.context == RNS.Packet.REQUEST:
try:
request_id = packet.getTruncatedHash()
packed_request = self.decrypt(packet.data)
unpacked_request = umsgpack.unpackb(packed_request)
self.handle_request(request_id, unpacked_request)
if packed_request != None:
unpacked_request = umsgpack.unpackb(packed_request)
self.handle_request(request_id, unpacked_request)
self.__update_phy_stats(packet, query_shared=True)
except Exception as e:
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.RESPONSE:
try:
packed_response = self.decrypt(packet.data)
unpacked_response = umsgpack.unpackb(packed_response)
request_id = unpacked_response[0]
response_data = unpacked_response[1]
transfer_size = len(umsgpack.packb(response_data))-2
self.handle_response(request_id, response_data, transfer_size, transfer_size)
if packed_response != None:
unpacked_response = umsgpack.unpackb(packed_response)
request_id = unpacked_response[0]
response_data = unpacked_response[1]
transfer_size = len(umsgpack.packb(response_data))-2
self.handle_response(request_id, response_data, transfer_size, transfer_size)
self.__update_phy_stats(packet, query_shared=True)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LRRTT:
if not self.initiator:
self.rtt_packet(packet)
self.__update_phy_stats(packet, query_shared=True)
elif packet.context == RNS.Packet.LINKCLOSE:
self.teardown_packet(packet)
self.__update_phy_stats(packet, query_shared=True)
elif packet.context == RNS.Packet.RESOURCE_ADV:
packet.plaintext = self.decrypt(packet.data)
if packet.plaintext != None:
self.__update_phy_stats(packet, query_shared=True)
if RNS.ResourceAdvertisement.is_request(packet):
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
elif RNS.ResourceAdvertisement.is_response(packet):
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
for pending_request in self.pending_requests:
if pending_request.request_id == request_id:
RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id)
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
pending_request.response_transfer_size = RNS.ResourceAdvertisement.read_transfer_size(packet)
pending_request.started_at = time.time()
elif self.resource_strategy == Link.ACCEPT_NONE:
pass
elif self.resource_strategy == Link.ACCEPT_APP:
if self.callbacks.resource != None:
try:
resource_advertisement = RNS.ResourceAdvertisement.unpack(packet.plaintext)
resource_advertisement.link = self
if self.callbacks.resource(resource_advertisement):
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
except Exception as e:
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
elif self.resource_strategy == Link.ACCEPT_ALL:
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
if RNS.ResourceAdvertisement.is_request(packet):
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
elif RNS.ResourceAdvertisement.is_response(packet):
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
for pending_request in self.pending_requests:
if pending_request.request_id == request_id:
response_resource = RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id)
if response_resource != None:
if pending_request.response_size == None:
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
if pending_request.response_transfer_size == None:
pending_request.response_transfer_size = 0
pending_request.response_transfer_size += RNS.ResourceAdvertisement.read_transfer_size(packet)
if pending_request.started_at == None:
pending_request.started_at = time.time()
pending_request.response_resource_progress(response_resource)
elif self.resource_strategy == Link.ACCEPT_NONE:
pass
elif self.resource_strategy == Link.ACCEPT_APP:
if self.callbacks.resource != None:
try:
resource_advertisement = RNS.ResourceAdvertisement.unpack(packet.plaintext)
resource_advertisement.link = self
if self.callbacks.resource(resource_advertisement):
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
except Exception as e:
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
elif self.resource_strategy == Link.ACCEPT_ALL:
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
elif packet.context == RNS.Packet.RESOURCE_REQ:
plaintext = self.decrypt(packet.data)
if ord(plaintext[:1]) == RNS.Resource.HASHMAP_IS_EXHAUSTED:
resource_hash = plaintext[1+RNS.Resource.MAPHASH_LEN:RNS.Identity.HASHLENGTH//8+1+RNS.Resource.MAPHASH_LEN]
else:
resource_hash = plaintext[1:RNS.Identity.HASHLENGTH//8+1]
if plaintext != None:
self.__update_phy_stats(packet, query_shared=True)
if ord(plaintext[:1]) == RNS.Resource.HASHMAP_IS_EXHAUSTED:
resource_hash = plaintext[1+RNS.Resource.MAPHASH_LEN:RNS.Identity.HASHLENGTH//8+1+RNS.Resource.MAPHASH_LEN]
else:
resource_hash = plaintext[1:RNS.Identity.HASHLENGTH//8+1]
for resource in self.outgoing_resources:
if resource.hash == resource_hash:
# We need to check that this request has not been
# received before in order to avoid sequencing errors.
if not packet.packet_hash in resource.req_hashlist:
resource.req_hashlist.append(packet.packet_hash)
resource.request(plaintext)
for resource in self.outgoing_resources:
if resource.hash == resource_hash:
# We need to check that this request has not been
# received before in order to avoid sequencing errors.
if not packet.packet_hash in resource.req_hashlist:
resource.req_hashlist.append(packet.packet_hash)
resource.request(plaintext)
elif packet.context == RNS.Packet.RESOURCE_HMU:
plaintext = self.decrypt(packet.data)
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
for resource in self.incoming_resources:
if resource_hash == resource.hash:
resource.hashmap_update_packet(plaintext)
if plaintext != None:
self.__update_phy_stats(packet, query_shared=True)
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
for resource in self.incoming_resources:
if resource_hash == resource.hash:
resource.hashmap_update_packet(plaintext)
elif packet.context == RNS.Packet.RESOURCE_ICL:
plaintext = self.decrypt(packet.data)
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
for resource in self.incoming_resources:
if resource_hash == resource.hash:
resource.cancel()
if plaintext != None:
self.__update_phy_stats(packet)
resource_hash = plaintext[:RNS.Identity.HASHLENGTH//8]
for resource in self.incoming_resources:
if resource_hash == resource.hash:
resource.cancel()
elif packet.context == RNS.Packet.KEEPALIVE:
if not self.initiator and packet.data == bytes([0xFF]):
keepalive_packet = RNS.Packet(self, bytes([0xFE]), context=RNS.Packet.KEEPALIVE)
keepalive_packet.send()
self.had_outbound()
self.had_outbound(is_keepalive = True)
# TODO: find the most efficient way to allow multiple
@@ -822,27 +924,17 @@ class Link:
elif packet.context == RNS.Packet.RESOURCE:
for resource in self.incoming_resources:
resource.receive_part(packet)
self.__update_phy_stats(packet)
elif packet.context == RNS.Packet.CHANNEL:
if not self._channel:
RNS.log(f"Channel data received without open channel", RNS.LOG_DEBUG)
else:
# TODO: Remove packet loss simulator ######
# if not hasattr(self, "drop_counter"):
# self.drop_counter = 0
# self.drop_counter += 1
# if self.drop_counter%6 == 0:
# RNS.log("Dropping channel packet for testing", RNS.LOG_DEBUG)
# else:
# packet.prove()
# plaintext = self.decrypt(packet.data)
# self._channel._receive(plaintext)
############################################
packet.prove()
plaintext = self.decrypt(packet.data)
self._channel._receive(plaintext)
if plaintext != None:
self.__update_phy_stats(packet)
self._channel._receive(plaintext)
elif packet.packet_type == RNS.Packet.PROOF:
if packet.context == RNS.Packet.RESOURCE_PRF:
@@ -850,6 +942,7 @@ class Link:
for resource in self.outgoing_resources:
if resource_hash == resource.hash:
resource.validate_proof(packet.data)
self.__update_phy_stats(packet, query_shared=True)
self.watchdog_lock = False
@@ -879,6 +972,7 @@ class Link:
except Exception as e:
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
return None
def sign(self, message):
@@ -1059,7 +1153,8 @@ class RequestReceipt():
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
self.started_at = time.time()
if self.started_at == None:
self.started_at = time.time()
self.status = RequestReceipt.DELIVERED
self.__resource_response_timeout = time.time()+self.timeout
response_timeout_thread = threading.Thread(target=self.__response_timeout_job)
@@ -1100,24 +1195,26 @@ class RequestReceipt():
def response_resource_progress(self, resource):
if not self.status == RequestReceipt.FAILED:
self.status = RequestReceipt.RECEIVING
if self.packet_receipt != None:
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
self.packet_receipt.proved = True
self.packet_receipt.concluded_at = time.time()
if self.packet_receipt.callbacks.delivery != None:
self.packet_receipt.callbacks.delivery(self.packet_receipt)
if resource != None:
if not self.status == RequestReceipt.FAILED:
self.status = RequestReceipt.RECEIVING
if self.packet_receipt != None:
if self.packet_receipt.status != RNS.PacketReceipt.DELIVERED:
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
self.packet_receipt.proved = True
self.packet_receipt.concluded_at = time.time()
if self.packet_receipt.callbacks.delivery != None:
self.packet_receipt.callbacks.delivery(self.packet_receipt)
self.progress = resource.get_progress()
if self.callbacks.progress != None:
try:
self.callbacks.progress(self)
except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
else:
resource.cancel()
self.progress = resource.get_progress()
if self.callbacks.progress != None:
try:
self.callbacks.progress(self)
except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
else:
resource.cancel()
def response_received(self, response):
+5 -4
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@ class Packet:
"""
The Packet class is used to create packet instances that can be sent
over a Reticulum network. Packets will automatically be encrypted if
they are adressed to a ``RNS.Destination.SINGLE`` destination,
they are addressed to a ``RNS.Destination.SINGLE`` destination,
``RNS.Destination.GROUP`` destination or a :ref:`RNS.Link<api-link>`.
For ``RNS.Destination.GROUP`` destinations, Reticulum will use the
@@ -138,6 +138,7 @@ class Packet:
self.receiving_interface = None
self.rssi = None
self.snr = None
self.q = None
def get_packed_flags(self):
if self.context == Packet.LRPROOF:
@@ -370,8 +371,8 @@ class PacketReceipt:
if packet.destination.type == RNS.Destination.LINK:
self.timeout = packet.destination.rtt * packet.destination.traffic_timeout_factor
else:
self.timeout = Packet.TIMEOUT_PER_HOP * RNS.Transport.hops_to(self.destination.hash)
self.timeout = RNS.Reticulum.get_instance().get_first_hop_timeout(self.destination.hash)
self.timeout += Packet.TIMEOUT_PER_HOP * RNS.Transport.hops_to(self.destination.hash)
def get_status(self):
"""
+27
View File
@@ -0,0 +1,27 @@
# MIT License
#
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
class Resolver:
@staticmethod
def resolve_identity(full_name):
pass
+127 -97
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@ import os
import bz2
import math
import time
import tempfile
import threading
from threading import Lock
from .vendor import umsgpack as umsgpack
from time import sleep
@@ -47,7 +49,7 @@ class Resource:
WINDOW = 4
# Absolute minimum window size during transfer
WINDOW_MIN = 1
WINDOW_MIN = 2
# The maximum window size for transfers on slow links
WINDOW_MAX_SLOW = 10
@@ -103,9 +105,10 @@ class Resource:
PART_TIMEOUT_FACTOR = 4
PART_TIMEOUT_FACTOR_AFTER_RTT = 2
MAX_RETRIES = 8
MAX_RETRIES = 16
MAX_ADV_RETRIES = 4
SENDER_GRACE_TIME = 10
SENDER_GRACE_TIME = 10.0
PROCESSING_GRACE = 1.0
RETRY_GRACE_TIME = 0.25
PER_RETRY_DELAY = 0.5
@@ -167,15 +170,13 @@ class Resource:
resource.hashmap = [None] * resource.total_parts
resource.hashmap_height = 0
resource.waiting_for_hmu = False
resource.receiving_part = False
resource.consecutive_completed_height = 0
resource.consecutive_completed_height = -1
if not resource.link.has_incoming_resource(resource):
resource.link.register_incoming_resource(resource)
RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
RNS.log(f"Accepting resource advertisement for {RNS.prettyhexrep(resource.hash)}. Transfer size is {RNS.prettysize(resource.size)} in {resource.total_parts} parts.", RNS.LOG_DEBUG)
if resource.link.callbacks.resource_started != None:
try:
resource.link.callbacks.resource_started(resource)
@@ -204,9 +205,19 @@ class Resource:
resource_data = None
self.assembly_lock = False
if data != None:
if not hasattr(data, "read") and len(data) > Resource.MAX_EFFICIENT_SIZE:
original_data = data
data_size = len(original_data)
data = tempfile.TemporaryFile()
data.write(original_data)
del original_data
if hasattr(data, "read"):
data_size = os.stat(data.name).st_size
self.total_size = data_size
if data_size == None:
data_size = os.stat(data.name).st_size
self.total_size = data_size
self.grand_total_parts = math.ceil(data_size/Resource.SDU)
if data_size <= Resource.MAX_EFFICIENT_SIZE:
@@ -279,7 +290,7 @@ class Resource:
self.uncompressed_data = data
compression_began = time.time()
if (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE):
if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE):
RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
self.compressed_data = bz2.compress(self.uncompressed_data)
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
@@ -366,7 +377,8 @@ class Resource:
if advertise:
self.advertise()
else:
pass
self.receive_lock = Lock()
def hashmap_update_packet(self, plaintext):
if not self.status == Resource.FAILED:
@@ -440,7 +452,7 @@ class Resource:
sleep_time = None
if self.status == Resource.ADVERTISED:
sleep_time = (self.adv_sent+self.timeout)-time.time()
sleep_time = (self.adv_sent+self.timeout+Resource.PROCESSING_GRACE)-time.time()
if sleep_time < 0:
if self.retries_left <= 0:
RNS.log("Resource transfer timeout after sending advertisement", RNS.LOG_DEBUG)
@@ -456,7 +468,7 @@ class Resource:
self.adv_sent = self.last_activity
sleep_time = 0.001
except Exception as e:
RNS.log("Could not resend advertisement packet, cancelling resource", RNS.LOG_VERBOSE)
RNS.log("Could not resend advertisement packet, cancelling resource. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
self.cancel()
@@ -612,10 +624,26 @@ class Resource:
self.callback(self)
except Exception as e:
RNS.log("Error while executing resource concluded callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
finally:
try:
if hasattr(self, "input_file"):
if hasattr(self.input_file, "close") and callable(self.input_file.close):
self.input_file.close()
except Exception as e:
RNS.log("Error while closing resource input file: "+str(e), RNS.LOG_ERROR)
else:
# Otherwise we'll recursively create the
# next segment of the resource
Resource(self.input_file, self.link, callback = self.callback, segment_index = self.segment_index+1, original_hash=self.original_hash, progress_callback = self.__progress_callback)
Resource(
self.input_file, self.link,
callback = self.callback,
segment_index = self.segment_index+1,
original_hash=self.original_hash,
progress_callback = self.__progress_callback,
request_id = self.request_id,
is_response = self.is_response,
)
else:
pass
else:
@@ -623,99 +651,99 @@ class Resource:
def receive_part(self, packet):
while self.receiving_part:
sleep(0.001)
with self.receive_lock:
self.receiving_part = True
self.last_activity = time.time()
self.retries_left = self.max_retries
self.receiving_part = True
self.last_activity = time.time()
self.retries_left = self.max_retries
if self.req_resp == None:
self.req_resp = self.last_activity
rtt = self.req_resp-self.req_sent
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
if self.rtt == None:
self.rtt = self.link.rtt
self.watchdog_job()
elif rtt < self.rtt:
self.rtt = max(self.rtt - self.rtt*0.05, rtt)
elif rtt > self.rtt:
self.rtt = min(self.rtt + self.rtt*0.05, rtt)
if self.req_resp == None:
self.req_resp = self.last_activity
rtt = self.req_resp-self.req_sent
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
if self.rtt == None:
self.rtt = self.link.rtt
self.watchdog_job()
elif rtt < self.rtt:
self.rtt = max(self.rtt - self.rtt*0.05, rtt)
elif rtt > self.rtt:
self.rtt = min(self.rtt + self.rtt*0.05, rtt)
if rtt > 0:
req_resp_cost = len(packet.raw)+self.req_sent_bytes
self.req_resp_rtt_rate = req_resp_cost / rtt
if rtt > 0:
req_resp_cost = len(packet.raw)+self.req_sent_bytes
self.req_resp_rtt_rate = req_resp_cost / rtt
if self.req_resp_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
self.fast_rate_rounds += 1
if self.req_resp_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
self.fast_rate_rounds += 1
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
self.window_max = Resource.WINDOW_MAX_FAST
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
self.window_max = Resource.WINDOW_MAX_FAST
if not self.status == Resource.FAILED:
self.status = Resource.TRANSFERRING
part_data = packet.data
part_hash = self.get_map_hash(part_data)
if not self.status == Resource.FAILED:
self.status = Resource.TRANSFERRING
part_data = packet.data
part_hash = self.get_map_hash(part_data)
i = self.consecutive_completed_height
for map_hash in self.hashmap[self.consecutive_completed_height:self.consecutive_completed_height+self.window]:
if map_hash == part_hash:
if self.parts[i] == None:
consecutive_index = self.consecutive_completed_height if self.consecutive_completed_height >= 0 else 0
i = consecutive_index
for map_hash in self.hashmap[consecutive_index:consecutive_index+self.window]:
if map_hash == part_hash:
if self.parts[i] == None:
# Insert data into parts list
self.parts[i] = part_data
self.rtt_rxd_bytes += len(part_data)
self.received_count += 1
self.outstanding_parts -= 1
# Insert data into parts list
self.parts[i] = part_data
self.rtt_rxd_bytes += len(part_data)
self.received_count += 1
self.outstanding_parts -= 1
# Update consecutive completed pointer
if i == self.consecutive_completed_height + 1:
self.consecutive_completed_height = i
cp = self.consecutive_completed_height + 1
while cp < len(self.parts) and self.parts[cp] != None:
self.consecutive_completed_height = cp
cp += 1
# Update consecutive completed pointer
if i == self.consecutive_completed_height + 1:
self.consecutive_completed_height = i
cp = self.consecutive_completed_height + 1
while cp < len(self.parts) and self.parts[cp] != None:
self.consecutive_completed_height = cp
cp += 1
if self.__progress_callback != None:
try:
self.__progress_callback(self)
except Exception as e:
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
if self.__progress_callback != None:
try:
self.__progress_callback(self)
except Exception as e:
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
i += 1
i += 1
self.receiving_part = False
self.receiving_part = False
if self.received_count == self.total_parts and not self.assembly_lock:
self.assembly_lock = True
self.assemble()
elif self.outstanding_parts == 0:
# TODO: Figure out if there is a mathematically
# optimal way to adjust windows
if self.window < self.window_max:
self.window += 1
if (self.window - self.window_min) > (self.window_flexibility-1):
self.window_min += 1
if self.received_count == self.total_parts and not self.assembly_lock:
self.assembly_lock = True
self.assemble()
elif self.outstanding_parts == 0:
# TODO: Figure out if there is a mathematically
# optimal way to adjust windows
if self.window < self.window_max:
self.window += 1
if (self.window - self.window_min) > (self.window_flexibility-1):
self.window_min += 1
if self.req_sent != 0:
rtt = time.time()-self.req_sent
req_transferred = self.rtt_rxd_bytes - self.rtt_rxd_bytes_at_part_req
if self.req_sent != 0:
rtt = time.time()-self.req_sent
req_transferred = self.rtt_rxd_bytes - self.rtt_rxd_bytes_at_part_req
if rtt != 0:
self.req_data_rtt_rate = req_transferred/rtt
self.rtt_rxd_bytes_at_part_req = self.rtt_rxd_bytes
if rtt != 0:
self.req_data_rtt_rate = req_transferred/rtt
self.rtt_rxd_bytes_at_part_req = self.rtt_rxd_bytes
if self.req_data_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
self.fast_rate_rounds += 1
if self.req_data_rtt_rate > Resource.RATE_FAST and self.fast_rate_rounds < Resource.FAST_RATE_THRESHOLD:
self.fast_rate_rounds += 1
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
self.window_max = Resource.WINDOW_MAX_FAST
if self.fast_rate_rounds == Resource.FAST_RATE_THRESHOLD:
self.window_max = Resource.WINDOW_MAX_FAST
self.request_next()
else:
self.receiving_part = False
self.request_next()
else:
self.receiving_part = False
# Called on incoming resource to send a request for more data
def request_next(self):
@@ -728,11 +756,11 @@ class Resource:
hashmap_exhausted = Resource.HASHMAP_IS_NOT_EXHAUSTED
requested_hashes = b""
offset = (1 if self.consecutive_completed_height > 0 else 0)
i = 0; pn = self.consecutive_completed_height+offset
i = 0; pn = self.consecutive_completed_height+1
search_start = pn
for part in self.parts[search_start:search_start+self.window]:
search_size = self.window
for part in self.parts[search_start:search_start+search_size]:
if part == None:
part_hash = self.hashmap[pn]
if part_hash != None:
@@ -752,7 +780,6 @@ class Resource:
hmu_part += last_map_hash
self.waiting_for_hmu = True
requested_data = b""
request_data = hmu_part + self.hash + requested_hashes
request_packet = RNS.Packet(self.link, request_data, context = RNS.Packet.RESOURCE_REQ)
@@ -908,8 +935,7 @@ class Resource:
else:
self.progress_total_parts = float(self.total_parts)
progress = self.processed_parts / self.progress_total_parts
progress = min(1.0, self.processed_parts / self.progress_total_parts)
return progress
def get_transfer_size(self):
@@ -997,6 +1023,7 @@ class ResourceAdvertisement:
def __init__(self, resource=None, request_id=None, is_response=False):
self.link = None
if resource != None:
self.t = resource.size # Transfer size
self.d = resource.total_size # Total uncompressed data size
@@ -1043,6 +1070,9 @@ class ResourceAdvertisement:
def is_compressed(self):
return self.c
def get_link(self):
return self.link
def pack(self, segment=0):
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
hashmap_end = min((segment+1)*(ResourceAdvertisement.HASHMAP_MAX_LEN), self.n)
+151 -11
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -79,7 +79,7 @@ class Reticulum:
MTU = 500
"""
The MTU that Reticulum adheres to, and will expect other peers to
adhere to. By default, the MTU is 507 bytes. In custom RNS network
adhere to. By default, the MTU is 500 bytes. In custom RNS network
implementations, it is possible to change this value, but doing so will
completely break compatibility with all other RNS networks. An identical
MTU is a prerequisite for peers to communicate in the same network.
@@ -106,17 +106,19 @@ class Reticulum:
it will eventually be dropped.
This value will be applied by default to all created interfaces,
but it can be configured individually on a per-interface basis.
but it can be configured individually on a per-interface basis. In
general, the global default setting should not be changed, and any
alterations should be made on a per-interface basis instead.
"""
MINIMUM_BITRATE = 500
MINIMUM_BITRATE = 5
"""
Minimum bitrate required across a medium for Reticulum to be able
to successfully establish links. Currently 5 bits per second.
"""
# TODO: To reach the 300bps level without unreasonably impacting
# performance on faster links, we need a mechanism for setting
# this value more intelligently. One option could be inferring it
# from interface speed, but a better general approach would most
# probably be to let Reticulum somehow continously build a map of
# per-hop latencies and use this map for the timeout calculation.
# TODO: Let Reticulum somehow continously build a map of per-hop
# latencies and use this map for global timeout calculation.
DEFAULT_PER_HOP_TIMEOUT = 6
# Length of truncated hashes in bits.
@@ -145,6 +147,8 @@ class Reticulum:
configpath = ""
storagepath = ""
cachepath = ""
__instance = None
@staticmethod
def exit_handler():
@@ -168,6 +172,13 @@ class Reticulum:
RNS.exit()
@staticmethod
def get_instance():
"""
Return the currently running Reticulum instance
"""
return Reticulum.__instance
def __init__(self,configdir=None, loglevel=None, logdest=None, verbosity=None):
"""
Initialises and starts a Reticulum instance. This must be
@@ -177,6 +188,11 @@ class Reticulum:
:param configdir: Full path to a Reticulum configuration directory.
"""
if Reticulum.__instance != None:
raise OSError("Attempt to reinitialise Reticulum, when it was already running")
else:
Reticulum.__instance = self
RNS.vendor.platformutils.platform_checks()
if configdir != None:
@@ -201,6 +217,7 @@ class Reticulum:
Reticulum.__transport_enabled = False
Reticulum.__use_implicit_proof = True
Reticulum.__allow_probes = False
Reticulum.panic_on_interface_error = False
@@ -208,6 +225,7 @@ class Reticulum:
self.local_control_port = 37429
self.share_instance = True
self.rpc_listener = None
self.rpc_key = None
self.ifac_salt = Reticulum.IFAC_SALT
@@ -261,7 +279,8 @@ class Reticulum:
RNS.Transport.start(self)
self.rpc_addr = ("127.0.0.1", self.local_control_port)
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
if self.rpc_key == None:
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
if self.is_shared_instance:
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
@@ -300,6 +319,10 @@ class Reticulum:
self.local_interface_port
)
interface.OUT = True
if hasattr(Reticulum, "_force_shared_instance_bitrate"):
interface.bitrate = Reticulum._force_shared_instance_bitrate
interface._force_bitrate = Reticulum._force_shared_instance_bitrate
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
RNS.Transport.interfaces.append(interface)
self.is_shared_instance = True
@@ -314,11 +337,16 @@ class Reticulum:
self.local_interface_port)
interface.target_port = self.local_interface_port
interface.OUT = True
if hasattr(Reticulum, "_force_shared_instance_bitrate"):
interface.bitrate = Reticulum._force_shared_instance_bitrate
interface._force_bitrate = True
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
RNS.Transport.interfaces.append(interface)
self.is_shared_instance = False
self.is_standalone_instance = False
self.is_connected_to_shared_instance = True
Reticulum.__transport_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)
@@ -357,10 +385,24 @@ class Reticulum:
if option == "instance_control_port":
value = int(self.config["reticulum"][option])
self.local_control_port = value
if option == "rpc_key":
try:
value = bytes.fromhex(self.config["reticulum"][option])
self.rpc_key = value
except Exception as e:
RNS.log("Invalid shared instance RPC key specified, falling back to default key", RNS.LOG_ERROR)
self.rpc_key = None
if option == "enable_transport":
v = self.config["reticulum"].as_bool(option)
if v == True:
Reticulum.__transport_enabled = True
if option == "respond_to_probes":
v = self.config["reticulum"].as_bool(option)
if v == True:
Reticulum.__allow_probes = True
if option == "force_shared_instance_bitrate":
v = self.config["reticulum"].as_int(option)
Reticulum._force_shared_instance_bitrate = v
if option == "panic_on_interface_error":
v = self.config["reticulum"].as_bool(option)
if v == True:
@@ -441,6 +483,23 @@ class Reticulum:
if c["pass_phrase"] != "":
ifac_netkey = c["pass_phrase"]
ingress_control = True
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
ic_max_held_announces = None
if "ic_max_held_announces" in c: ic_max_held_announces = c.as_int("ic_max_held_announces")
ic_burst_hold = None
if "ic_burst_hold" in c: ic_burst_hold = c.as_float("ic_burst_hold")
ic_burst_freq_new = None
if "ic_burst_freq_new" in c: ic_burst_freq_new = c.as_float("ic_burst_freq_new")
ic_burst_freq = None
if "ic_burst_freq" in c: ic_burst_freq = c.as_float("ic_burst_freq")
ic_new_time = None
if "ic_new_time" in c: ic_new_time = c.as_float("ic_new_time")
ic_burst_penalty = None
if "ic_burst_penalty" in c: ic_burst_penalty = c.as_float("ic_burst_penalty")
ic_held_release_interval = None
if "ic_held_release_interval" in c: ic_held_release_interval = c.as_float("ic_held_release_interval")
configured_bitrate = None
if "bitrate" in c:
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
@@ -481,6 +540,7 @@ class Reticulum:
group_id = c["group_id"] if "group_id" in c else None
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
multicast_address_type = c["multicast_address_type"] if "multicast_address_type" in c else None
data_port = int(c["data_port"]) if "data_port" in c else None
allowed_interfaces = c.as_list("devices") if "devices" in c else None
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
@@ -491,6 +551,7 @@ class Reticulum:
group_id,
discovery_scope,
discovery_port,
multicast_address_type,
data_port,
allowed_interfaces,
ignored_interfaces
@@ -878,6 +939,14 @@ class Reticulum:
interface.announce_rate_target = announce_rate_target
interface.announce_rate_grace = announce_rate_grace
interface.announce_rate_penalty = announce_rate_penalty
interface.ingress_control = ingress_control
if ic_max_held_announces != None: interface.ic_max_held_announces = ic_max_held_announces
if ic_burst_hold != None: interface.ic_burst_hold = ic_burst_hold
if ic_burst_freq_new != None: interface.ic_burst_freq_new = ic_burst_freq_new
if ic_burst_freq != None: interface.ic_burst_freq = ic_burst_freq
if ic_new_time != None: interface.ic_new_time = ic_new_time
if ic_burst_penalty != None: interface.ic_burst_penalty = ic_burst_penalty
if ic_held_release_interval != None: interface.ic_held_release_interval = ic_held_release_interval
interface.ifac_netname = ifac_netname
interface.ifac_netkey = ifac_netkey
@@ -1034,18 +1103,27 @@ class Reticulum:
if path == "next_hop":
rpc_connection.send(self.get_next_hop(call["destination_hash"]))
if path == "first_hop_timeout":
rpc_connection.send(self.get_first_hop_timeout(call["destination_hash"]))
if path == "packet_rssi":
rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
if path == "packet_snr":
rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
if path == "packet_q":
rpc_connection.send(self.get_packet_q(call["packet_hash"]))
if "drop" in call:
path = call["drop"]
if path == "path":
rpc_connection.send(self.drop_path(call["destination_hash"]))
if path == "all_via":
rpc_connection.send(self.drop_all_via(call["destination_hash"]))
if path == "announce_queues":
rpc_connection.send(self.drop_announce_queues())
@@ -1137,6 +1215,9 @@ class Reticulum:
ifstats["name"] = str(interface)
ifstats["rxb"] = interface.rxb
ifstats["txb"] = interface.txb
ifstats["incoming_announce_frequency"] = interface.incoming_announce_frequency()
ifstats["outgoing_announce_frequency"] = interface.outgoing_announce_frequency()
ifstats["held_announces"] = len(interface.held_announces)
ifstats["status"] = interface.online
ifstats["mode"] = interface.mode
@@ -1147,6 +1228,10 @@ class Reticulum:
if Reticulum.transport_enabled():
stats["transport_id"] = RNS.Transport.identity.hash
stats["transport_uptime"] = time.time()-RNS.Transport.start_time
if Reticulum.probe_destination_enabled():
stats["probe_responder"] = RNS.Transport.probe_destination.hash
else:
stats["probe_responder"] = None
return stats
@@ -1203,6 +1288,22 @@ class Reticulum:
else:
return RNS.Transport.expire_path(destination)
def drop_all_via(self, transport_hash):
if self.is_connected_to_shared_instance:
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
rpc_connection.send({"drop": "all_via", "destination_hash": transport_hash})
response = rpc_connection.recv()
return response
else:
dropped_count = 0
for destination_hash in RNS.Transport.destination_table:
if RNS.Transport.destination_table[destination_hash][1] == transport_hash:
RNS.Transport.expire_path(destination_hash)
dropped_count += 1
return dropped_count
def drop_announce_queues(self):
if self.is_connected_to_shared_instance:
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
@@ -1223,6 +1324,26 @@ class Reticulum:
else:
return str(RNS.Transport.next_hop_interface(destination))
def get_first_hop_timeout(self, destination):
if self.is_connected_to_shared_instance:
try:
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
rpc_connection.send({"get": "first_hop_timeout", "destination_hash": destination})
response = rpc_connection.recv()
if self.is_connected_to_shared_instance and hasattr(self, "_force_shared_instance_bitrate") and self._force_shared_instance_bitrate:
simulated_latency = ((1/self._force_shared_instance_bitrate)*8)*RNS.Reticulum.MTU
RNS.log("Adding simulated latency of "+RNS.prettytime(simulated_latency)+" to first hop timeout", RNS.LOG_DEBUG)
response += simulated_latency
return response
except Exception as e:
RNS.log("An error occurred while getting first hop timeout from shared instance: "+str(e), RNS.LOG_ERROR)
return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
else:
return RNS.Transport.first_hop_timeout(destination)
def get_next_hop(self, destination):
if self.is_connected_to_shared_instance:
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
@@ -1262,6 +1383,21 @@ class Reticulum:
return None
def get_packet_q(self, packet_hash):
if self.is_connected_to_shared_instance:
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
rpc_connection.send({"get": "packet_q", "packet_hash": packet_hash})
response = rpc_connection.recv()
return response
else:
for entry in RNS.Transport.local_client_q_cache:
if entry[0] == packet_hash:
return entry[1]
return None
@staticmethod
def should_use_implicit_proof():
"""
@@ -1285,6 +1421,10 @@ class Reticulum:
"""
return Reticulum.__transport_enabled
@staticmethod
def probe_destination_enabled():
return Reticulum.__allow_probes
# Default configuration file:
__default_rns_config__ = '''# This is the default Reticulum config file.
# You should probably edit it to include any additional,
+360 -79
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -64,15 +64,21 @@ class Transport:
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds
PATH_REQUEST_GRACE = 0.35 # Grace time before a path announcement is made, allows directly reachable peers to respond first
PATH_REQUEST_RW = 2 # Path request random window
PATH_REQUEST_MI = 5 # Minimum interval in seconds for automated path requests
PATH_REQUEST_GRACE = 0.4 # Grace time before a path announcement is made, allows directly reachable peers to respond first
PATH_REQUEST_RG = 0.6 # Extra grace time for roaming-mode interfaces to allow more suitable peers to respond first
PATH_REQUEST_MI = 20 # Minimum interval in seconds for automated path requests
STATE_UNKNOWN = 0x00
STATE_UNRESPONSIVE = 0x01
STATE_RESPONSIVE = 0x02
LINK_TIMEOUT = RNS.Link.STALE_TIME * 1.25
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after 30 minutes
DESTINATION_TIMEOUT = 60*60*24*7 # Destination table entries are removed if unused for one week
MAX_RECEIPTS = 1024 # Maximum number of receipts to keep track of
MAX_RATE_TIMESTAMPS = 16 # Maximum number of announce timestamps to keep per destination
PERSIST_RANDOM_BLOBS = 32 # Maximum number of random blobs per destination to persist to disk
MAX_RANDOM_BLOBS = 64 # Maximum number of random blobs per destination to keep in memory
interfaces = [] # All active interfaces
destinations = [] # All active destinations
@@ -94,6 +100,7 @@ class Transport:
tunnels = {} # A table storing tunnels to other transport instances
announce_rate_table = {} # A table for keeping track of announce rates
path_requests = {} # A table for storing path request timestamps
path_states = {} # A table for keeping track of path states
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
discovery_pr_tags = [] # A table for keeping track of tagged path requests
@@ -111,6 +118,7 @@ class Transport:
local_client_rssi_cache = []
local_client_snr_cache = []
local_client_q_cache = []
LOCAL_CLIENT_CACHE_MAXSIZE = 512
pending_local_path_requests = {}
@@ -128,6 +136,8 @@ class Transport:
hashlist_maxsize = 1000000
tables_last_culled = 0.0
tables_cull_interval = 5.0
interface_last_jobs = 0.0
interface_jobs_interval = 5.0
identity = None
@@ -270,6 +280,15 @@ class Transport:
except Exception as e:
RNS.log("Could not load tunnel table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
if RNS.Reticulum.probe_destination_enabled():
Transport.probe_destination = RNS.Destination(Transport.identity, RNS.Destination.IN, RNS.Destination.SINGLE, Transport.APP_NAME, "probe")
Transport.probe_destination.accepts_links(False)
Transport.probe_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
Transport.probe_destination.announce()
RNS.log("Transport Instance will respond to probe requests on "+str(Transport.probe_destination), RNS.LOG_NOTICE)
else:
Transport.probe_destination = None
RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE)
Transport.start_time = time.time()
@@ -288,7 +307,8 @@ class Transport:
@staticmethod
def jobs():
outgoing = []
path_requests = []
path_requests = {}
blocked_if = None
Transport.jobs_running = True
try:
@@ -316,7 +336,8 @@ class Transport:
if time.time() - last_path_request > Transport.PATH_REQUEST_MI:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link.destination.hash)+" since an attempted link was never established", RNS.LOG_DEBUG)
if not link.destination.hash in path_requests:
path_requests.append(link.destination.hash)
blocked_if = None
path_requests[link.destination.hash] = blocked_if
Transport.pending_links.remove(link)
@@ -343,12 +364,12 @@ class Transport:
# Process announces needing retransmission
if time.time() > Transport.announces_last_checked+Transport.announces_check_interval:
completed_announces = []
for destination_hash in Transport.announce_table:
announce_entry = Transport.announce_table[destination_hash]
if announce_entry[2] > Transport.PATHFINDER_R:
RNS.log("Completed announce processing for "+RNS.prettyhexrep(destination_hash)+", retry limit reached", RNS.LOG_EXTREME)
Transport.announce_table.pop(destination_hash)
break
completed_announces.append(destination_hash)
else:
if time.time() > announce_entry[1]:
announce_entry[1] = time.time() + Transport.PATHFINDER_G + Transport.PATHFINDER_RW
@@ -395,6 +416,10 @@ class Transport:
Transport.announce_table[destination_hash] = held_entry
RNS.log("Reinserting held announce into table", RNS.LOG_DEBUG)
for destination_hash in completed_announces:
if destination_hash in Transport.announce_table:
Transport.announce_table.pop(destination_hash)
Transport.announces_last_checked = time.time()
@@ -407,6 +432,12 @@ class Transport:
Transport.discovery_pr_tags = Transport.discovery_pr_tags[len(Transport.discovery_pr_tags)-Transport.max_pr_tags:len(Transport.discovery_pr_tags)-1]
if time.time() > Transport.tables_last_culled + Transport.tables_cull_interval:
# Remove unneeded path state entries
stale_path_states = []
for destination_hash in Transport.path_states:
if not destination_hash in Transport.destination_table:
stale_path_states.append(destination_hash)
# Cull the reverse table according to timeout
stale_reverse_entries = []
for truncated_packet_hash in Transport.reverse_table:
@@ -430,14 +461,49 @@ class Transport:
if link_entry[6] in Transport.path_requests:
last_path_request = Transport.path_requests[link_entry[6]]
lr_taken_hops = link_entry[5]
path_request_throttle = time.time() - last_path_request < Transport.PATH_REQUEST_MI
path_request_conditions = False
# If the path has been invalidated between the time of
# making the link request and now, try to rediscover it
if not Transport.has_path(link_entry[6]):
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and path is now missing", RNS.LOG_DEBUG)
path_request_conditions =True
# If this link request was originated from a local client
# attempt to rediscover a path to the destination, if this
# has not already happened recently.
lr_taken_hops = link_entry[5]
if lr_taken_hops == 0 and time.time() - last_path_request > Transport.PATH_REQUEST_MI:
elif not path_request_throttle and lr_taken_hops == 0:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted local client link was never established", RNS.LOG_DEBUG)
path_request_conditions = True
# If the link destination was previously only 1 hop
# away, this likely means that it was local to one
# of our interfaces, and that it roamed somewhere else.
# In that case, try to discover a new path.
elif not path_request_throttle and Transport.hops_to(link_entry[6]) == 1:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and destination was previously local to an interface on this instance", RNS.LOG_DEBUG)
path_request_conditions = True
blocked_if = link_entry[4]
# If the link initiator is only 1 hop away,
# this likely means that network topology has
# changed. In that case, we try to discover a new path,
# and mark the old one as potentially unresponsive.
elif not path_request_throttle and lr_taken_hops == 1:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and link initiator is local to an interface on this instance", RNS.LOG_DEBUG)
path_request_conditions = True
blocked_if = link_entry[4]
if RNS.Reticulum.transport_enabled():
if hasattr(link_entry[4], "mode") and link_entry[4].mode != RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
Transport.mark_path_unresponsive(link_entry[6])
if path_request_conditions:
if not link_entry[6] in path_requests:
path_requests.append(link_entry[6])
path_requests[link_entry[6]] = blocked_if
if not RNS.Reticulum.transport_enabled():
# Drop current path if we are not a transport instance, to
@@ -505,8 +571,6 @@ class Transport:
else:
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME)
i = 0
for truncated_packet_hash in stale_reverse_entries:
Transport.reverse_table.pop(truncated_packet_hash)
@@ -518,8 +582,6 @@ class Transport:
else:
RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME)
i = 0
for link_id in stale_links:
Transport.link_table.pop(link_id)
@@ -564,8 +626,24 @@ class Transport:
else:
RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_EXTREME)
i = 0
for destination_hash in stale_path_states:
Transport.path_states.pop(destination_hash)
i += 1
if i > 0:
if i == 1:
RNS.log("Removed "+str(i)+" path state entry", RNS.LOG_EXTREME)
else:
RNS.log("Removed "+str(i)+" path state entries", RNS.LOG_EXTREME)
Transport.tables_last_culled = time.time()
if time.time() > Transport.interface_last_jobs + Transport.interface_jobs_interval:
for interface in Transport.interfaces:
interface.process_held_announces()
Transport.interface_last_jobs = time.time()
else:
# Transport jobs were locked, do nothing
pass
@@ -580,7 +658,17 @@ class Transport:
packet.send()
for destination_hash in path_requests:
Transport.request_path(destination_hash)
blocked_if = path_requests[destination_hash]
if blocked_if == None:
Transport.request_path(destination_hash)
else:
for interface in Transport.interfaces:
if interface != blocked_if:
# RNS.log("Transmitting path request on "+str(interface), RNS.LOG_DEBUG)
Transport.request_path(destination_hash, on_interface=interface)
else:
pass
# RNS.log("Blocking path request on "+str(interface), RNS.LOG_DEBUG)
@staticmethod
def transmit(interface, raw):
@@ -637,6 +725,30 @@ class Transport:
sent = False
outbound_time = time.time()
generate_receipt = False
if (packet.create_receipt == True and
# Only generate receipts for DATA packets
packet.packet_type == RNS.Packet.DATA and
# Don't generate receipts for PLAIN destinations
packet.destination.type != RNS.Destination.PLAIN and
# Don't generate receipts for link-related packets
not (packet.context >= RNS.Packet.KEEPALIVE and packet.context <= RNS.Packet.LRPROOF) and
# Don't generate receipts for resource packets
not (packet.context >= RNS.Packet.RESOURCE and packet.context <= RNS.Packet.RESOURCE_RCL)):
generate_receipt = True
def packet_sent(packet):
packet.sent = True
packet.sent_at = time.time()
if generate_receipt:
packet.receipt = RNS.PacketReceipt(packet)
Transport.receipts.append(packet.receipt)
# TODO: Enable when caching has been redesigned
# Transport.cache(packet)
# Check if we have a known path for the destination in the path table
if packet.packet_type != RNS.Packet.ANNOUNCE and packet.destination.type != RNS.Destination.PLAIN and packet.destination.type != RNS.Destination.GROUP and packet.destination_hash in Transport.destination_table:
outbound_interface = Transport.destination_table[packet.destination_hash][5]
@@ -654,6 +766,7 @@ class Transport:
new_raw += packet.raw[1:2]
new_raw += Transport.destination_table[packet.destination_hash][1]
new_raw += packet.raw[2:]
packet_sent(packet)
Transport.transmit(outbound_interface, new_raw)
Transport.destination_table[packet.destination_hash][0] = time.time()
sent = True
@@ -673,6 +786,7 @@ class Transport:
new_raw += packet.raw[1:2]
new_raw += Transport.destination_table[packet.destination_hash][1]
new_raw += packet.raw[2:]
packet_sent(packet)
Transport.transmit(outbound_interface, new_raw)
Transport.destination_table[packet.destination_hash][0] = time.time()
sent = True
@@ -681,6 +795,7 @@ class Transport:
# directly reachable, and also on which interface, so we
# simply transmit the packet directly on that one.
else:
packet_sent(packet)
Transport.transmit(outbound_interface, packet.raw)
sent = True
@@ -764,7 +879,7 @@ class Transport:
interface.announce_queue = []
queued_announces = True if len(interface.announce_queue) > 0 else False
if not queued_announces and outbound_time > interface.announce_allowed_at:
if not queued_announces and outbound_time > interface.announce_allowed_at and interface.bitrate != None and interface.bitrate != 0:
tx_time = (len(packet.raw)*8) / interface.bitrate
wait_time = (tx_time / interface.announce_cap)
interface.announce_allowed_at = outbound_time + wait_time
@@ -845,28 +960,11 @@ class Transport:
# thread.start()
Transport.transmit(interface, packet.raw)
if packet.packet_type == RNS.Packet.ANNOUNCE:
interface.sent_announce()
packet_sent(packet)
sent = True
if sent:
packet.sent = True
packet.sent_at = time.time()
# Don't generate receipt if it has been explicitly disabled
if (packet.create_receipt == True and
# Only generate receipts for DATA packets
packet.packet_type == RNS.Packet.DATA and
# Don't generate receipts for PLAIN destinations
packet.destination.type != RNS.Destination.PLAIN and
# Don't generate receipts for link-related packets
not (packet.context >= RNS.Packet.KEEPALIVE and packet.context <= RNS.Packet.LRPROOF) and
# Don't generate receipts for resource packets
not (packet.context >= RNS.Packet.RESOURCE and packet.context <= RNS.Packet.RESOURCE_RCL)):
packet.receipt = RNS.PacketReceipt(packet)
Transport.receipts.append(packet.receipt)
Transport.cache(packet)
Transport.jobs_locked = False
return sent
@@ -875,6 +973,13 @@ class Transport:
# TODO: Think long and hard about this.
# Is it even strictly necessary with the current
# transport rules?
# Filter packets intended for other transport instances
if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE:
if packet.transport_id != Transport.identity.hash:
RNS.log("Ignored packet "+RNS.prettyhexrep(packet.packet_hash)+" in transport for other transport instance", RNS.LOG_EXTREME)
return False
if packet.context == RNS.Packet.KEEPALIVE:
return True
if packet.context == RNS.Packet.RESOURCE_REQ:
@@ -998,6 +1103,7 @@ class Transport:
packet = RNS.Packet(None, raw)
if not packet.unpack():
Transport.jobs_locked = False
return
packet.receiving_interface = interface
@@ -1011,7 +1117,7 @@ class Transport:
Transport.local_client_rssi_cache.append([packet.packet_hash, packet.rssi])
while len(Transport.local_client_rssi_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_rssi_cache.pop()
Transport.local_client_rssi_cache.pop(0)
if hasattr(interface, "r_stat_snr"):
if interface.r_stat_rssi != None:
@@ -1020,7 +1126,16 @@ class Transport:
Transport.local_client_snr_cache.append([packet.packet_hash, packet.snr])
while len(Transport.local_client_snr_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_snr_cache.pop()
Transport.local_client_snr_cache.pop(0)
if hasattr(interface, "r_stat_q"):
if interface.r_stat_q != None:
packet.q = interface.r_stat_q
if len(Transport.local_client_interfaces) > 0:
Transport.local_client_q_cache.append([packet.packet_hash, packet.q])
while len(Transport.local_client_q_cache) > Transport.LOCAL_CLIENT_CACHE_MAXSIZE:
Transport.local_client_q_cache.pop(0)
if len(Transport.local_client_interfaces) > 0:
if Transport.is_local_client_interface(interface):
@@ -1029,10 +1144,31 @@ class Transport:
elif Transport.interface_to_shared_instance(interface):
packet.hops -= 1
if Transport.packet_filter(packet):
Transport.packet_hashlist.append(packet.packet_hash)
Transport.cache(packet)
# By default, remember packet hashes to avoid routing
# loops in the network, using the packet filter.
remember_packet_hash = True
# If this packet belongs to a link in our link table,
# we'll have to defer adding it to the filter list.
# In some cases, we might see a packet over a shared-
# medium interface, belonging to a link that transports
# or terminates with this instance, but before it would
# normally reach us. If the packet is appended to the
# filter list at this point, link transport will break.
if packet.destination_hash in Transport.link_table:
remember_packet_hash = False
# If this is a link request proof, don't add it until
# we are sure it's not actually somewhere else in the
# routing chain.
if packet.packet_type == RNS.Packet.PROOF and packet.context == RNS.Packet.LRPROOF:
remember_packet_hash = False
if remember_packet_hash:
Transport.packet_hashlist.append(packet.packet_hash)
# TODO: Enable when caching has been redesigned
# Transport.cache(packet)
# Check special conditions for local clients connected
# through a shared Reticulum instance
@@ -1079,6 +1215,7 @@ class Transport:
# normal processing.
if packet.context == RNS.Packet.CACHE_REQUEST:
if Transport.cache_request_packet(packet):
Transport.jobs_locked = False
return
# If the packet is in transport, check whether we
@@ -1112,7 +1249,8 @@ class Transport:
if packet.packet_type == RNS.Packet.LINKREQUEST:
now = time.time()
proof_timeout = now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops)
proof_timeout = Transport.extra_link_proof_timeout(packet.receiving_interface)
proof_timeout += now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops)
# Entry format is
link_entry = [ now, # 0: Timestamp,
@@ -1151,7 +1289,7 @@ class Transport:
link_entry = Transport.link_table[packet.destination_hash]
# If receiving and outbound interface is
# the same for this link, direction doesn't
# matter, and we simply send the packet on.
# matter, and we simply repeat the packet.
outbound_interface = None
if link_entry[2] == link_entry[4]:
# But check that taken hops matches one
@@ -1172,6 +1310,11 @@ class Transport:
outbound_interface = link_entry[2]
if outbound_interface != None:
# Add this packet to the filter hashlist if we
# have determined that it's actually our turn
# to process it.
Transport.packet_hashlist.append(packet.packet_hash)
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
@@ -1185,8 +1328,21 @@ class Transport:
# announces, queueing rebroadcasts of these, and removal
# of queued announce rebroadcasts once handed to the next node.
if packet.packet_type == RNS.Packet.ANNOUNCE:
if interface != None and RNS.Identity.validate_announce(packet, only_validate_signature=True):
interface.received_announce()
if not packet.destination_hash in Transport.destination_table:
# This is an unknown destination, and we'll apply
# potential ingress limiting. Already known
# destinations will have re-announces controlled
# by normal announce rate limiting.
if interface.should_ingress_limit():
interface.hold_announce(packet)
Transport.jobs_locked = False
return
local_destination = next((d for d in Transport.destinations if d.hash == packet.destination_hash), None)
if local_destination == None and RNS.Identity.validate_announce(packet):
if local_destination == None and RNS.Identity.validate_announce(packet):
if packet.transport_id != None:
received_from = packet.transport_id
@@ -1201,7 +1357,8 @@ class Transport:
announce_entry[6] += 1
if announce_entry[6] >= Transport.LOCAL_REBROADCASTS_MAX:
RNS.log("Max local rebroadcasts of announce for "+RNS.prettyhexrep(packet.destination_hash)+" reached, dropping announce from our table", RNS.LOG_DEBUG)
Transport.announce_table.pop(packet.destination_hash)
if packet.destination_hash in Transport.announce_table:
Transport.announce_table.pop(packet.destination_hash)
if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0:
now = time.time()
@@ -1236,6 +1393,7 @@ class Transport:
# TODO: Check whether this approach works
# under all circumstances
if not random_blob in random_blobs:
Transport.mark_path_unknown_state(packet.destination_hash)
should_add = True
else:
should_add = False
@@ -1253,21 +1411,39 @@ class Transport:
if path_announce_emitted >= announce_emitted:
break
# If the path has expired, consider this
# announce for adding to the path table.
if (now >= path_expires):
# We also check that the announce is
# We check that the announce is
# different from ones we've already heard,
# to avoid loops in the network
if not random_blob in random_blobs:
# TODO: Check that this ^ approach actually
# works under all circumstances
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce due to expired path", RNS.LOG_DEBUG)
Transport.mark_path_unknown_state(packet.destination_hash)
should_add = True
else:
should_add = False
else:
# If the path is not expired, but the emission
# is more recent, and we haven't already heard
# this announce before, update the path table.
if (announce_emitted > path_announce_emitted):
if not random_blob in random_blobs:
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since it was more recently emitted", RNS.LOG_DEBUG)
Transport.mark_path_unknown_state(packet.destination_hash)
should_add = True
else:
should_add = False
# If we have already heard this announce before,
# but the path has been marked as unresponsive
# by a failed communications attempt or similar,
# allow updating the path table to this one.
elif announce_emitted == path_announce_emitted:
if Transport.path_is_unresponsive(packet.destination_hash):
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since previously tried path was unresponsive", RNS.LOG_DEBUG)
should_add = True
else:
should_add = False
@@ -1278,7 +1454,7 @@ class Transport:
should_add = True
if should_add:
now = time.time()
now = time.time()
rate_blocked = False
if packet.context != RNS.Packet.PATH_RESPONSE and packet.receiving_interface.announce_rate_target != None:
@@ -1331,6 +1507,7 @@ class Transport:
expires = now + Transport.PATHFINDER_E
random_blobs.append(random_blob)
random_blobs = random_blobs[-Transport.MAX_RANDOM_BLOBS:]
if (RNS.Reticulum.transport_enabled() or Transport.from_local_client(packet)) and packet.context != RNS.Packet.PATH_RESPONSE:
# Insert announce into announce table for retransmission
@@ -1535,38 +1712,58 @@ class Transport:
# needs to be transported
if (RNS.Reticulum.transport_enabled() or for_local_client_link or from_local_client) and packet.destination_hash in Transport.link_table:
link_entry = Transport.link_table[packet.destination_hash]
if packet.receiving_interface == link_entry[2]:
try:
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
peer_identity = RNS.Identity.recall(link_entry[6])
peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
if packet.hops == link_entry[3]:
if packet.receiving_interface == link_entry[2]:
try:
if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2:
peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2]
peer_identity = RNS.Identity.recall(link_entry[6])
peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE]
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if peer_identity.validate(signature, signed_data):
RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
Transport.link_table[packet.destination_hash][7] = True
Transport.transmit(link_entry[4], new_raw)
if peer_identity.validate(signature, signed_data):
RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME)
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
Transport.link_table[packet.destination_hash][7] = True
Transport.transmit(link_entry[4], new_raw)
else:
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
else:
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e:
RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR)
else:
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
else:
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
RNS.log("Received link request proof with hop mismatch, not transporting it", RNS.LOG_DEBUG)
else:
# Check if we can deliver it to a local
# pending link
for link in Transport.pending_links:
if link.link_id == packet.destination_hash:
link.validate_proof(packet)
# We need to also allow an expected hops value of
# PATHFINDER_M, since in some cases, the number of hops
# to the destination will be unknown at link creation
# time. The real chance of this occuring is likely to be
# extremely small, and this allowance could probably
# be discarded without major issues, but it is kept
# for now to ensure backwards compatibility.
# TODO: Probably reset check back to
# if packet.hops == link.expected_hops:
# within one of the next releases
if packet.hops == link.expected_hops or link.expected_hops == RNS.Transport.PATHFINDER_M:
# Add this packet to the filter hashlist if we
# have determined that it's actually destined
# for this system, and then validate the proof
Transport.packet_hashlist.append(packet.packet_hash)
link.validate_proof(packet)
elif packet.context == RNS.Packet.RESOURCE_PRF:
for link in Transport.active_links:
@@ -1583,7 +1780,7 @@ class Transport:
else:
proof_hash = None
# Check if this proof neds to be transported
# Check if this proof needs to be transported
if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table:
reverse_entry = Transport.reverse_table.pop(packet.destination_hash)
if packet.receiving_interface == reverse_entry[1]:
@@ -1602,9 +1799,6 @@ class Transport:
if receipt.hash == proof_hash:
receipt_validated = receipt.validate_proof_packet(packet)
else:
# TODO: This looks like it should actually
# be rewritten when implicit proofs are added.
# In case of an implicit proof, we have
# to check every single outstanding receipt
receipt_validated = receipt.validate_proof_packet(packet)
@@ -1782,8 +1976,11 @@ class Transport:
@staticmethod
def should_cache(packet):
if packet.context == RNS.Packet.RESOURCE_PRF:
return True
# TODO: Rework the caching system. It's currently
# not very useful to even cache Resource proofs,
# disabling it for now, until redesigned.
# if packet.context == RNS.Packet.RESOURCE_PRF:
# return True
return False
@@ -1906,6 +2103,45 @@ class Transport:
else:
return None
@staticmethod
def next_hop_interface_bitrate(destination_hash):
next_hop_interface = Transport.next_hop_interface(destination_hash)
if next_hop_interface != None:
return next_hop_interface.bitrate
else:
return None
@staticmethod
def next_hop_per_bit_latency(destination_hash):
next_hop_interface_bitrate = Transport.next_hop_interface_bitrate(destination_hash)
if next_hop_interface_bitrate != None:
return (1/next_hop_interface_bitrate)
else:
return None
@staticmethod
def next_hop_per_byte_latency(destination_hash):
per_bit_latency = Transport.next_hop_per_bit_latency(destination_hash)
if per_bit_latency != None:
return per_bit_latency*8
else:
return None
@staticmethod
def first_hop_timeout(destination_hash):
latency = Transport.next_hop_per_byte_latency(destination_hash)
if latency != None:
return RNS.Reticulum.MTU * latency + RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
else:
return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
@staticmethod
def extra_link_proof_timeout(interface):
if interface != None:
return ((1/interface.bitrate)*8)*RNS.Reticulum.MTU
else:
return 0
@staticmethod
def expire_path(destination_hash):
if destination_hash in Transport.destination_table:
@@ -1915,6 +2151,38 @@ class Transport:
else:
return False
@staticmethod
def mark_path_unresponsive(destination_hash):
if destination_hash in Transport.destination_table:
Transport.path_states[destination_hash] = Transport.STATE_UNRESPONSIVE
return True
else:
return False
@staticmethod
def mark_path_responsive(destination_hash):
if destination_hash in Transport.destination_table:
Transport.path_states[destination_hash] = Transport.STATE_RESPONSIVE
return True
else:
return False
@staticmethod
def mark_path_unknown_state(destination_hash):
if destination_hash in Transport.destination_table:
Transport.path_states[destination_hash] = Transport.STATE_UNKNOWN
return True
else:
return False
@staticmethod
def path_is_unresponsive(destination_hash):
if destination_hash in Transport.path_states:
if Transport.path_states[destination_hash] == Transport.STATE_UNRESPONSIVE:
return True
return False
@staticmethod
def request_path(destination_hash, on_interface=None, tag=None, recursive=False):
"""
@@ -2072,8 +2340,18 @@ class Transport:
if is_from_local_client:
retransmit_timeout = now
else:
# TODO: Look at this timing
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
if Transport.is_local_client_interface(Transport.next_hop_interface(destination_hash)):
RNS.log("Path request destination "+RNS.prettyhexrep(destination_hash)+" is on a local client interface, rebroadcasting immediately", RNS.LOG_EXTREME)
retransmit_timeout = now
else:
retransmit_timeout = now + Transport.PATH_REQUEST_GRACE
# If we are answering on a roaming-mode interface, wait a
# little longer, to allow potential more well-connected
# peers to answer first.
if attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
retransmit_timeout += Transport.PATH_REQUEST_RG
# This handles an edge case where a peer sends a past
# request for a destination just after an announce for
@@ -2169,7 +2447,10 @@ class Transport:
pass
for interface in detachable_interfaces:
interface.detach()
try:
interface.detach()
except Exception as e:
RNS.log("An error occurred while detaching "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
@staticmethod
def shared_connection_disappeared():
@@ -2367,7 +2648,7 @@ class Transport:
received_from = de[1]
hops = de[2]
expires = de[3]
random_blobs = de[4]
random_blobs = de[4][-Transport.PERSIST_RANDOM_BLOBS:]
packet_hash = de[6].get_hash()
serialised_entry = [
+10 -4
View File
@@ -321,7 +321,7 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash))
else:
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
listener_identity = RNS.Identity.recall(destination_hash)
listener_destination = RNS.Destination(
@@ -579,11 +579,17 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
sys.stdout.flush()
i = (i+1)%len(syms)
if not RNS.Transport.has_path(destination_hash):
if time.time() > estab_timeout:
if silent:
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
print("Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out")
else:
print("\r \rCould not establish link with "+RNS.prettyhexrep(destination_hash))
print("\r \rLink establishment with "+RNS.prettyhexrep(destination_hash)+" timed out")
exit(1)
elif not RNS.Transport.has_path(destination_hash):
if silent:
print("No path found to "+RNS.prettyhexrep(destination_hash))
else:
print("\r \rNo path found to "+RNS.prettyhexrep(destination_hash))
exit(1)
else:
if silent:
+90 -9
View File
@@ -64,19 +64,22 @@ def main():
parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
parser.add_argument("-i", "--identity", metavar="identity", action="store", default=None, help="hexadecimal Reticulum Destination hash or path to Identity file", type=str)
parser.add_argument("-g", "--generate", metavar="path", action="store", default=None, help="generate a new Identity")
parser.add_argument("-g", "--generate", metavar="file", action="store", default=None, help="generate a new Identity")
parser.add_argument("-m", "--import", dest="import_str", metavar="identity_data", action="store", default=None, help="import Reticulum identity in hex, base32 or base64 format", type=str)
parser.add_argument("-x", "--export", action="store_true", default=None, help="export identity to hex, base32 or base64 format")
parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
parser.add_argument("-a", "--announce", metavar="aspects", action="store", default=None, help="announce a destination based on this Identity")
parser.add_argument("-H", "--hash", metavar="aspects", action="store", default=None, help="show destination hashes for other aspects for this Identity")
parser.add_argument("-e", "--encrypt", metavar="path", action="store", default=None, help="encrypt file")
parser.add_argument("-d", "--decrypt", metavar="path", action="store", default=None, help="decrypt file")
parser.add_argument("-e", "--encrypt", metavar="file", action="store", default=None, help="encrypt file")
parser.add_argument("-d", "--decrypt", metavar="file", action="store", default=None, help="decrypt file")
parser.add_argument("-s", "--sign", metavar="path", action="store", default=None, help="sign file")
parser.add_argument("-V", "--validate", metavar="path", action="store", default=None, help="validate signature")
parser.add_argument("-r", "--read", metavar="path", action="store", default=None, help="input file path", type=str)
parser.add_argument("-w", "--write", metavar="path", action="store", default=None, help="output file path", type=str)
parser.add_argument("-r", "--read", metavar="file", action="store", default=None, help="input file path", type=str)
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
@@ -86,7 +89,8 @@ def main():
parser.add_argument("-p", "--print-identity", action="store_true", default=False, help="print identity info and exit")
parser.add_argument("-P", "--print-private", action="store_true", default=False, help="allow displaying private keys")
parser.add_argument("-b", "--base64", action="store_true", default=False, help=argparse.SUPPRESS) # help="Use base64-encoded input and output")
parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output")
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
@@ -110,6 +114,59 @@ def main():
args.read = args.sign
identity_str = args.identity
if args.import_str:
identity_bytes = None
try:
if args.base64:
identity_bytes = base64.urlsafe_b64decode(args.import_str)
elif args.base32:
identity_bytes = base64.b32decode(args.import_str)
else:
identity_bytes = bytes.fromhex(args.import_str)
except Exception as e:
print("Invalid identity data specified for import: "+str(e))
exit(41)
try:
identity = RNS.Identity.from_bytes(identity_bytes)
except Exception as e:
print("Could not create Reticulum identity from specified data: "+str(e))
exit(42)
RNS.log("Identity imported")
if args.base64:
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
elif args.base32:
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
else:
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
if identity.prv:
if args.print_private:
if args.base64:
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
elif args.base32:
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
else:
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
else:
RNS.log("Private Key : Hidden")
if args.write:
try:
wp = os.path.expanduser(args.write)
if not os.path.isfile(wp) or args.force:
identity.to_file(wp)
RNS.log("Wrote imported identity to "+str(args.write))
else:
print("File "+str(wp)+" already exists, not overwriting")
exit(43)
except Exception as e:
print("Error while writing imported identity to file: "+str(e))
exit(44)
exit(0)
if not args.generate and not identity_str:
print("\nNo identity provided, cannot continue\n")
parser.print_help()
@@ -196,7 +253,7 @@ def main():
if args.hash:
try:
aspects = args.hash.split(".")
if not len(aspects) > 1:
if not len(aspects) > 0:
RNS.log("Invalid destination aspects specified", RNS.LOG_ERROR)
exit(32)
else:
@@ -246,14 +303,38 @@ def main():
exit(0)
if args.print_identity:
RNS.log("Public Key : "+RNS.hexrep(identity.pub_bytes, delimit=False))
if args.base64:
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
elif args.base32:
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
else:
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
if identity.prv:
if args.print_private:
RNS.log("Private Key : "+RNS.hexrep(identity.prv_bytes, delimit=False))
if args.base64:
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
elif args.base32:
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
else:
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
else:
RNS.log("Private Key : Hidden")
exit(0)
if args.export:
if identity.prv:
if args.base64:
RNS.log("Exported Identity : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
elif args.base32:
RNS.log("Exported Identity : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
else:
RNS.log("Exported Identity : "+RNS.hexrep(identity.get_private_key(), delimit=False))
else:
RNS.log("Identity doesn't hold a private key, cannot export")
exit(50)
exit(0)
if args.validate:
if not args.read and args.validate.lower().endswith("."+SIG_EXT):
args.read = str(args.validate).replace("."+SIG_EXT, "")
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2023 Mark Qvist / unsigned.io
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import RNS
import argparse
import time
from RNS._version import __version__
def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
targetverbosity = verbosity-quietness
if service:
targetlogdest = RNS.LOG_FILE
targetverbosity = None
else:
targetlogdest = RNS.LOG_STDOUT
reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
exit(0)
def main():
try:
parser = argparse.ArgumentParser(description="Reticulum Distributed Identity Resolver")
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('-q', '--quiet', action='count', default=0)
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__))
args = parser.parse_args()
if args.exampleconfig:
print(__example_rns_config__)
exit()
if args.config:
configarg = args.config
else:
configarg = None
program_setup(configdir = configarg, verbosity=args.verbose, quietness=args.quiet)
except KeyboardInterrupt:
print("")
exit()
__example_rns_config__ = '''# This is an example Identity Resolver file.
'''
if __name__ == "__main__":
main()
+437 -136
View File
@@ -41,9 +41,9 @@ import RNS
RNS.logtimefmt = "%H:%M:%S"
RNS.compact_log_fmt = True
program_version = "2.1.2"
eth_addr = "0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a"
btc_addr = "3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq"
program_version = "2.1.3"
eth_addr = "0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73"
btc_addr = "35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH"
xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
rnode = None
@@ -53,6 +53,7 @@ rnode_baudrate = 115200
known_keys = [["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100bf831ebd99f43b477caf1a094bec829389da40653e8f1f83fc14bf1b98a3e1cc70e759c213a43f71e5a47eb56a9ca487f241335b3e6ff7cdde0ee0a1c75c698574aeba0485726b6a9dfc046b4188e3520271ee8555a8f405cf21f81f2575771d0b0887adea5dd53c1f594f72c66b5f14904ffc2e72206a6698a490d51ba1105b0203010001"], ["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100e5d46084e445595376bf7efd9c6ccf19d39abbc59afdb763207e4ff68b8d00ebffb63847aa2fe6dd10783d3ea63b55ac66f71ad885c20e223709f0d51ed5c6c0d0b093be9e1d165bb8a483a548b67a3f7a1e4580f50e75b306593fa6067ae259d3e297717bd7ff8c8f5b07f2bed89929a9a0321026cf3699524db98e2d18fb2d020300ff39"]]
firmware_update_url = "https://github.com/markqvist/RNode_Firmware/releases/download/"
fw_filename = None
fw_url = None
mapped_model = None
class KISS():
@@ -80,6 +81,7 @@ class KISS():
CMD_BLINK = 0x30
CMD_RANDOM = 0x40
CMD_DISP_INT = 0x45
CMD_DISP_ADR = 0x63
CMD_BT_CTRL = 0x46
CMD_BT_PIN = 0x62
CMD_BOARD = 0x47
@@ -119,12 +121,16 @@ class KISS():
class ROM():
PLATFORM_AVR = 0x90
PLATFORM_ESP32 = 0x80
PLATFORM_NRF52 = 0x70
MCU_1284P = 0x91
MCU_2560 = 0x92
MCU_ESP32 = 0x81
MCU_NRF52 = 0x71
PRODUCT_RNODE = 0x03
MODEL_A1 = 0xA1
MODEL_A6 = 0xA6
MODEL_A4 = 0xA4
MODEL_A9 = 0xA9
MODEL_A3 = 0xA3
@@ -143,14 +149,23 @@ class ROM():
PRODUCT_T32_21 = 0xB1
MODEL_B4 = 0xB4
MODEL_B9 = 0xB9
MODEL_B4_TCXO = 0x04 # The TCXO model codes are only used here to select the
MODEL_B9_TCXO = 0x09 # correct firmware, actual model codes in firmware is
# still 0xB4 and 0xB9.
PRODUCT_H32_V2 = 0xC0
MODEL_C4 = 0xC4
MODEL_C9 = 0xC9
PRODUCT_H32_V3 = 0xC1
MODEL_C5 = 0xC5
MODEL_CA = 0xCA
PRODUCT_TBEAM = 0xE0
MODEL_E4 = 0xE4
MODEL_E9 = 0xE9
MODEL_E3 = 0xE3
MODEL_E8 = 0xE8
PRODUCT_HMBRW = 0xF0
MODEL_FF = 0xFF
@@ -181,6 +196,7 @@ class ROM():
BOARD_GENERIC_ESP32 = 0x35
BOARD_LORA32_V2_0 = 0x36
BOARD_LORA32_V2_1 = 0x37
BOARD_RAK4630 = 0x51
mapped_product = ROM.PRODUCT_RNODE
products = {
@@ -191,38 +207,49 @@ products = {
ROM.PRODUCT_T32_20: "LilyGO LoRa32 v2.0",
ROM.PRODUCT_T32_21: "LilyGO LoRa32 v2.1",
ROM.PRODUCT_H32_V2: "Heltec LoRa32 v2",
ROM.PRODUCT_H32_V3: "Heltec LoRa32 v3",
}
platforms = {
ROM.PLATFORM_AVR: "AVR",
ROM.PLATFORM_ESP32:"ESP32",
ROM.PLATFORM_NRF52:"NRF52",
}
mcus = {
ROM.MCU_1284P: "ATmega1284P",
ROM.MCU_2560:"ATmega2560",
ROM.MCU_ESP32:"Espressif Systems ESP32",
ROM.MCU_NRF52:"Nordic nRF52840",
}
models = {
0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex"],
0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex"],
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip"],
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip"],
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip"],
0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip"],
0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip"],
0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip"],
0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip"],
0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip"],
0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip"],
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip"],
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip"],
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip"],
0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip"],
0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip"],
0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None],
0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None],
0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex", "SX1278"],
0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex", "SX1276"],
0xA1: [410000000, 525000000, 22, "410 - 525 MHz", "rnode_firmware_t3s3.zip", "SX1268"],
0xA6: [820000000, 1020000000, 22, "820 - 960 MHz", "rnode_firmware_t3s3.zip", "SX1262"],
0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip", "SX1278"],
0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip", "SX1276"],
0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip", "SX1278"],
0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip", "SX1276"],
0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip", "SX1278"],
0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip", "SX1276"],
0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip", "SX1278"],
0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip", "SX1276"],
0x04: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1278"],
0x09: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1276"],
0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip", "SX1278"],
0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip", "SX1276"],
0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip", "SX1278"],
0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip", "SX1276"],
0xC5: [470000000, 510000000, 21, "470 - 510 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
0xCA: [863000000, 928000000, 21, "863 - 928 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip", "SX1278"],
0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip", "SX1276"],
0xE3: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1268"],
0xE8: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1262"],
0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None, "Unknown"],
0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None, "Unknown"],
}
CNF_DIR = None
@@ -515,7 +542,7 @@ class RNode():
if (len(command_buffer) == 4):
self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
elif (command == KISS.CMD_STAT_RSSI):
self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET
self.r_stat_rssi = byte-157 # RSSI Offset
elif (command == KISS.CMD_STAT_SNR):
self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
elif (command == KISS.CMD_RANDOM):
@@ -577,7 +604,14 @@ class RNode():
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_INT])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth enable command to device")
raise IOError("An IO error occurred while sending display intensity command to device")
def set_display_address(self, address):
data = bytes([address & 0xFF])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_ADR])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending display address command to device")
def enable_bluetooth(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
@@ -747,18 +781,13 @@ class RNode():
self.made = bytes([self.eeprom[ROM.ADDR_MADE], self.eeprom[ROM.ADDR_MADE+1], self.eeprom[ROM.ADDR_MADE+2], self.eeprom[ROM.ADDR_MADE+3]])
self.checksum = b""
self.min_freq = models[self.model][0]
self.max_freq = models[self.model][1]
self.max_output = models[self.model][2]
try:
self.min_freq = models[self.model][0]
self.max_freq = models[self.model][1]
self.max_output = models[self.model][2]
except Exception as e:
RNS.log("Exception")
RNS.log(str(e))
RNS.log("Error: Model band and output power capabilities are unknown!")
RNS.log("The contained exception was: "+str(e))
self.min_freq = 0
self.max_freq = 0
self.max_output = 0
@@ -920,6 +949,7 @@ class RNode():
except Exception as e:
self.provisioned = False
RNS.log("Invalid EEPROM data, could not parse device EEPROM.")
RNS.log("The contained exception was: "+str(e))
def device_probe(self):
@@ -972,56 +1002,92 @@ def ensure_firmware_file(fw_filename):
else:
try:
if selected_version == None:
if not upd_nocheck:
try:
if not upd_nocheck:
try:
# if custom firmware url, download latest release
if selected_version == None and fw_url == None:
version_url = firmware_version_url+fw_filename
RNS.log("Retrieving latest version info from "+version_url)
urlretrieve(firmware_version_url+fw_filename, UPD_DIR+"/"+fw_filename+".version.latest")
else:
if fw_url != None:
if selected_version == None:
version_url = fw_url+"latest/download/release.json"
else:
version_url = fw_url+"download/"+selected_version+"/release.json"
else:
version_url = firmware_update_url+selected_version+"/release.json"
try:
urlretrieve(firmware_version_url+fw_filename, UPD_DIR+"/"+fw_filename+".version.latest")
RNS.log("Retrieving specified version info from "+version_url)
urlretrieve(version_url, UPD_DIR+"/version_release_info.json")
import json
with open(UPD_DIR+"/version_release_info.json", "rb") as rif:
rdat = json.loads(rif.read())
variant = rdat[fw_filename]
with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
inf_str = str(variant["version"])+" "+str(variant["hash"])
verf.write(inf_str.encode("utf-8"))
except Exception as e:
RNS.log("")
RNS.log("WARNING!")
RNS.log("Failed to retrieve latest version information for your board from the default server")
RNS.log("Will retry using the following fallback URL: "+fallback_firmware_version_url)
RNS.log("")
RNS.log("Hit enter if you want to proceed")
input()
try:
urlretrieve(fallback_firmware_version_url, UPD_DIR+"/fallback_release_info.json")
import json
with open(UPD_DIR+"/fallback_release_info.json", "rb") as rif:
rdat = json.loads(rif.read())
variant = rdat[fw_filename]
with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
inf_str = str(variant["version"])+" "+str(variant["hash"])
verf.write(inf_str.encode("utf-8"))
except Exception as e:
RNS.log("Error while trying fallback URL: "+str(e))
raise e
except Exception as e:
RNS.log("Failed to retrive latest version information for your board.")
RNS.log("Failed to retrive version information for your board.")
RNS.log("Check your internet connection and try again.")
RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
exit()
except Exception as e:
# if custom firmware url, don't fallback
if fw_url != None:
RNS.log("Failed to retrive version information for your board from the specified url.")
RNS.log("Check your internet connection and try again.")
RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
exit()
import shutil
file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb")
release_info = file.read().decode("utf-8").strip()
selected_version = release_info.split()[0]
selected_hash = release_info.split()[1]
if not os.path.isdir(UPD_DIR+"/"+selected_version):
os.makedirs(UPD_DIR+"/"+selected_version)
shutil.copy(UPD_DIR+"/"+fw_filename+".version.latest", UPD_DIR+"/"+selected_version+"/"+fw_filename+".version")
RNS.log("The latest firmware for this board is version "+selected_version)
RNS.log("")
RNS.log("WARNING!")
RNS.log("Failed to retrieve latest version information for your board from the default server.")
RNS.log("Will retry using the following fallback URL: "+fallback_firmware_version_url)
RNS.log("")
RNS.log("Hit enter if you want to proceed")
input()
try:
urlretrieve(fallback_firmware_version_url, UPD_DIR+"/fallback_release_info.json")
import json
with open(UPD_DIR+"/fallback_release_info.json", "rb") as rif:
rdat = json.loads(rif.read())
variant = rdat[fw_filename]
with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
inf_str = str(variant["version"])+" "+str(variant["hash"])
verf.write(inf_str.encode("utf-8"))
else:
RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
RNS.log("use the --fw-version option to manually specify a version.")
exit(98)
except Exception as e:
RNS.log("Error while trying fallback URL: "+str(e))
raise e
update_target_url = firmware_update_url+selected_version+"/"+fw_filename
import shutil
file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb")
release_info = file.read().decode("utf-8").strip()
selected_version = release_info.split()[0]
if selected_version == "not":
RNS.log("No valid version found for this board, exiting.")
exit(199)
selected_hash = release_info.split()[1]
if not os.path.isdir(UPD_DIR+"/"+selected_version):
os.makedirs(UPD_DIR+"/"+selected_version)
shutil.copy(UPD_DIR+"/"+fw_filename+".version.latest", UPD_DIR+"/"+selected_version+"/"+fw_filename+".version")
RNS.log("The selected firmware for this board is version "+selected_version)
else:
RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
RNS.log("use the --fw-version option to manually specify a version.")
exit(98)
# if custom firmware url, use it
if fw_url != None:
update_target_url = fw_url+"download/"+selected_version+"/"+fw_filename
RNS.log("Retrieving firmware from custom url "+update_target_url)
else:
update_target_url = firmware_update_url+selected_version+"/"+fw_filename
try:
if not os.path.isdir(UPD_DIR+"/"+selected_version):
@@ -1098,7 +1164,7 @@ device_signer = None
force_update = False
upd_nocheck = False
def main():
global mapped_product, mapped_model, fw_filename, selected_version, force_update, upd_nocheck, device_signer
global mapped_product, mapped_model, fw_filename, fw_url, selected_version, force_update, upd_nocheck, device_signer
try:
if not util.find_spec("serial"):
@@ -1130,6 +1196,7 @@ def main():
parser.add_argument("-u", "--update", action="store_true", help="Update firmware to the latest version")
parser.add_argument("-U", "--force-update", action="store_true", help="Update to specified firmware even if version matches or is older than installed version")
parser.add_argument("--fw-version", action="store", metavar="version", default=None, help="Use a specific firmware version for update or autoinstall")
parser.add_argument("--fw-url", action="store", metavar="url", default=None, help="Use an alternate firmware download URL")
parser.add_argument("--nocheck", action="store_true", help="Don't check for firmware updates online")
parser.add_argument("-e", "--extract", action="store_true", help="Extract firmware from connected RNode for later use")
parser.add_argument("-E", "--use-extracted", action="store_true", help="Use the extracted firmware for autoinstallation or update")
@@ -1144,6 +1211,7 @@ def main():
parser.add_argument("-p", "--bluetooth-pair", action="store_true", help="Put device into bluetooth pairing mode")
parser.add_argument("-D", "--display", action="store", metavar="i", type=int, default=None, help="Set display intensity (0-255)")
parser.add_argument("--display-addr", action="store", metavar="byte", type=str, default=None, help="Set display address as hex byte (00 - FF)")
parser.add_argument("--freq", action="store", metavar="Hz", type=int, default=None, help="Frequency in Hz for TNC mode")
parser.add_argument("--bw", action="store", metavar="Hz", type=int, default=None, help="Bandwidth in Hz for TNC mode")
@@ -1160,15 +1228,15 @@ def main():
parser.add_argument("--version", action="store_true", help="Print program version and exit")
parser.add_argument("-f", "--flash", action="store_true", help=argparse.SUPPRESS) # Flash firmware and bootstrap EEPROM
parser.add_argument("-r", "--rom", action="store_true", help=argparse.SUPPRESS) # Bootstrap EEPROM without flashing firmware
parser.add_argument("-k", "--key", action="store_true", help=argparse.SUPPRESS) # Generate a new signing key and exit
parser.add_argument("-S", "--sign", action="store_true", help=argparse.SUPPRESS) # Display public part of signing key
parser.add_argument("-H", "--firmware-hash", action="store", help=argparse.SUPPRESS) # Display public part of signing key
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help=argparse.SUPPRESS) # Platform specification for device bootstrap
parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help=argparse.SUPPRESS) # Product specification for device bootstrap
parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help=argparse.SUPPRESS) # Model code for device bootstrap
parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help=argparse.SUPPRESS) # Hardware revision for device bootstrap
parser.add_argument("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM")
parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") #
parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash")
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap")
parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help="Product specification for device bootstrap") #
parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for device bootstrap")
parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision for device bootstrap")
parser.add_argument("port", nargs="?", default=None, help="serial port where RNode is attached", type=str)
args = parser.parse_args()
@@ -1201,6 +1269,9 @@ def main():
RNS.log("Selected version \""+selected_version+"\" does not appear to be a number.")
exit()
if args.fw_url != None:
fw_url = args.fw_url
if args.force_update:
force_update = True
@@ -1488,6 +1559,8 @@ def main():
print("[5] LilyGO LoRa32 v1.0")
print("[6] LilyGO T-Beam")
print("[7] Heltec LoRa32 v2")
print("[8] Heltec LoRa32 v3")
#print("[9] LilyGO LoRa T3S3")
print(" .")
print(" / \\ Select one of these options if you want to easily turn")
print(" | a supported development board into an RNode.")
@@ -1498,7 +1571,8 @@ def main():
selected_product = None
try:
c_dev = int(input())
if c_dev < 1 or c_dev > 7:
c_mod = False
if c_dev < 1 or c_dev > 9:
raise ValueError()
elif c_dev == 1:
selected_product = ROM.PRODUCT_RNODE
@@ -1527,8 +1601,7 @@ def main():
print(" T-Beam RNode Installer")
print("")
print("The RNode firmware can currently be installed on T-Beam devices using the")
print("SX1276 and SX1278 transceiver chips. Support for devices with the newer")
print("SX1262 and SX1268 chips is in development.")
print("SX1276, SX1278, SX1262 and SX1268 transceiver chips.")
print("")
print("Important! Using RNode firmware on T-Beam devices should currently be")
print("considered experimental. It is not intended for production or critical use.")
@@ -1599,6 +1672,39 @@ def main():
print("who would like to experiment with it. Hit enter to continue.")
print("---------------------------------------------------------------------------")
input()
elif c_dev == 9:
selected_product = ROM.PRODUCT_RNODE
c_mod = True
clear()
print("")
print("---------------------------------------------------------------------------")
print(" LilyGO LoRa32 T3S3 RNode Installer")
print("")
print("Important! Using RNode firmware on T3S3 devices should currently be")
print("considered experimental. It is not intended for production or critical use.")
print("")
print("Please note that Bluetooth is currently not implemented on this board.")
print("")
print("The currently supplied firmware is provided AS-IS as a courtesey to those")
print("who would like to experiment with it. Hit enter to continue.")
print("---------------------------------------------------------------------------")
input()
elif c_dev == 8:
selected_product = ROM.PRODUCT_H32_V3
clear()
print("")
print("---------------------------------------------------------------------------")
print(" Heltec LoRa32 v3.0 RNode Installer")
print("")
print("Important! Using RNode firmware on Heltec devices should currently be")
print("considered experimental. It is not intended for production or critical use.")
print("")
print("Please note that Bluetooth is currently not implemented on this board.")
print("")
print("The currently supplied firmware is provided AS-IS as a courtesey to those")
print("who would like to experiment with it. Hit enter to continue.")
print("---------------------------------------------------------------------------")
input()
except Exception as e:
print("That device type does not exist, exiting now.")
exit()
@@ -1651,53 +1757,84 @@ def main():
elif selected_product == ROM.PRODUCT_RNODE:
selected_mcu = ROM.MCU_1284P
print("\nWhat model is this RNode?\n")
print("[1] Handheld v2.x RNode, 410 - 525 MHz")
print("[2] Handheld v2.x RNode, 820 - 1020 MHz")
print("")
print("[3] Original v1.x RNode, 410 - 525 MHz")
print("[4] Original v1.x RNode, 820 - 1020 MHz")
# print("[5] Prototype v2 RNode, 410 - 525 MHz")
# print("[6] Prototype v2 RNode, 820 - 1020 MHz")
print("\n? ", end="")
try:
c_model = int(input())
if c_model < 1 or c_model > 6:
raise ValueError()
elif c_model == 3:
selected_model = ROM.MODEL_A4
selected_platform = ROM.PLATFORM_AVR
elif c_model == 4:
selected_model = ROM.MODEL_A9
selected_platform = ROM.PLATFORM_AVR
elif c_model == 1:
selected_model = ROM.MODEL_A2
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 2:
selected_model = ROM.MODEL_A7
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
# elif c_model == 5:
# selected_model = ROM.MODEL_A3
# selected_mcu = ROM.MCU_ESP32
# selected_platform = ROM.PLATFORM_ESP32
# elif c_model == 6:
# selected_model = ROM.MODEL_A8
# selected_mcu = ROM.MCU_ESP32
# selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That model does not exist, exiting now.")
exit()
if not c_mod:
selected_mcu = ROM.MCU_1284P
print("\nWhat model is this RNode?\n")
print("[1] Handheld v2.1 RNode, 410 - 525 MHz")
print("[2] Handheld v2.1 RNode, 820 - 1020 MHz")
print("")
print("[3] Original v1.x RNode, 410 - 525 MHz")
print("[4] Original v1.x RNode, 820 - 1020 MHz")
print("")
print("[5] Prototype v2.2 RNode, 410 - 525 MHz")
print("[6] Prototype v2.2 RNode, 820 - 1020 MHz")
print("\n? ", end="")
try:
c_model = int(input())
if c_model < 1 or c_model > 6:
raise ValueError()
elif c_model == 1:
selected_model = ROM.MODEL_A2
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 2:
selected_model = ROM.MODEL_A7
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 3:
selected_model = ROM.MODEL_A4
selected_platform = ROM.PLATFORM_AVR
elif c_model == 4:
selected_model = ROM.MODEL_A9
selected_platform = ROM.PLATFORM_AVR
elif c_model == 5:
selected_model = ROM.MODEL_A1
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 6:
selected_model = ROM.MODEL_A6
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
# elif c_model == 5:
# selected_model = ROM.MODEL_A3
# selected_mcu = ROM.MCU_ESP32
# selected_platform = ROM.PLATFORM_ESP32
# elif c_model == 6:
# selected_model = ROM.MODEL_A8
# selected_mcu = ROM.MCU_ESP32
# selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That model does not exist, exiting now.")
exit()
else:
print("\nWhat model is this T3S3?\n")
print("[1] 410 - 525 MHz (with SX1268 chip)")
print("[2] 820 - 1020 MHz (with SX1262 chip)")
print("\n? ", end="")
try:
c_model = int(input())
if c_model < 1 or c_model > 2:
raise ValueError()
elif c_model == 1:
selected_model = ROM.MODEL_A1
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 2:
selected_model = ROM.MODEL_A6
selected_mcu = ROM.MCU_ESP32
selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That model does not exist, exiting now.")
exit()
elif selected_product == ROM.PRODUCT_TBEAM:
selected_mcu = ROM.MCU_ESP32
print("\nWhat band is this T-Beam for?\n")
print("[1] 433 MHz")
print("[2] 868 MHz")
print("[3] 915 MHz")
print("[4] 923 MHz")
print("[1] 433 MHz (with SX1278 chip)")
print("[2] 868/915/923 MHz (with SX1276 chip)")
print("");
print("[3] 433 MHz (with SX1268 chip)")
print("[4] 868/915/923 MHz (with SX1262 chip)")
print("\n? ", end="")
try:
c_model = int(input())
@@ -1706,9 +1843,15 @@ def main():
elif c_model == 1:
selected_model = ROM.MODEL_E4
selected_platform = ROM.PLATFORM_ESP32
elif c_model > 1:
elif c_model == 2:
selected_model = ROM.MODEL_E9
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 3:
selected_model = ROM.MODEL_E3
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 4:
selected_model = ROM.MODEL_E8
selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That band does not exist, exiting now.")
exit()
@@ -1761,9 +1904,9 @@ def main():
selected_mcu = ROM.MCU_ESP32
print("\nWhat band is this LoRa32 for?\n")
print("[1] 433 MHz")
print("[2] 868 MHz")
print("[3] 915 MHz")
print("[4] 923 MHz")
print("[2] 868/915/923 MHz")
print("[3] 433 MHz, with TCXO")
print("[4] 868/915/923 MHz, with TCXO")
print("\n? ", end="")
try:
c_model = int(input())
@@ -1772,9 +1915,15 @@ def main():
elif c_model == 1:
selected_model = ROM.MODEL_B4
selected_platform = ROM.PLATFORM_ESP32
elif c_model > 1:
elif c_model == 2:
selected_model = ROM.MODEL_B9
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 3:
selected_model = ROM.MODEL_B4_TCXO
selected_platform = ROM.PLATFORM_ESP32
elif c_model == 4:
selected_model = ROM.MODEL_B9_TCXO
selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That band does not exist, exiting now.")
exit()
@@ -1801,6 +1950,28 @@ def main():
print("That band does not exist, exiting now.")
exit()
elif selected_product == ROM.PRODUCT_H32_V3:
selected_mcu = ROM.MCU_ESP32
print("\nWhat band is this Heltec LoRa32 V3 for?\n")
print("[1] 433 MHz")
print("[2] 868 MHz")
print("[3] 915 MHz")
print("[4] 923 MHz")
print("\n? ", end="")
try:
c_model = int(input())
if c_model < 1 or c_model > 4:
raise ValueError()
elif c_model == 1:
selected_model = ROM.MODEL_C5
selected_platform = ROM.PLATFORM_ESP32
elif c_model > 1:
selected_model = ROM.MODEL_CA
selected_platform = ROM.PLATFORM_ESP32
except Exception as e:
print("That band does not exist, exiting now.")
exit()
if selected_model != ROM.MODEL_FF and selected_model != ROM.MODEL_FE:
fw_filename = models[selected_model][4]
@@ -1828,7 +1999,7 @@ def main():
except Exception as e:
print("That ESP32 board does not exist, exiting now.")
exit()
if fw_filename == None:
print("")
print("Sorry, no firmware for your board currently exists.")
@@ -2089,6 +2260,42 @@ def main():
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
]
elif fw_filename == "rnode_firmware_tbeam_sx1262.zip":
if numeric_version >= 1.55:
return [
sys.executable, flasher,
"--chip", "esp32",
"--port", args.port,
"--baud", args.baud_flash,
"--before", "default_reset",
"--after", "hard_reset",
"write_flash", "-z",
"--flash_mode", "dio",
"--flash_freq", "80m",
"--flash_size", "4MB",
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.boot_app0",
"0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bin",
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.partitions",
]
else:
return [
sys.executable, flasher,
"--chip", "esp32",
"--port", args.port,
"--baud", args.baud_flash,
"--before", "default_reset",
"--after", "hard_reset",
"write_flash", "-z",
"--flash_mode", "dio",
"--flash_freq", "80m",
"--flash_size", "4MB",
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.boot_app0",
"0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
]
elif fw_filename == "rnode_firmware_lora32v10.zip":
if numeric_version >= 1.59:
return [
@@ -2197,6 +2404,24 @@ def main():
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.partitions",
]
elif fw_filename == "rnode_firmware_lora32v21_tcxo.zip":
return [
sys.executable, flasher,
"--chip", "esp32",
"--port", args.port,
"--baud", args.baud_flash,
"--before", "default_reset",
"--after", "hard_reset",
"write_flash", "-z",
"--flash_mode", "dio",
"--flash_freq", "80m",
"--flash_size", "4MB",
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.boot_app0",
"0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bin",
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.partitions",
]
elif fw_filename == "rnode_firmware_heltec32v2.zip":
if numeric_version >= 1.55:
return [
@@ -2233,6 +2458,23 @@ def main():
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.partitions",
]
elif fw_filename == "rnode_firmware_heltec32v3.zip":
return [
sys.executable, flasher,
"--chip", "esp32-s3",
"--port", args.port,
"--baud", args.baud_flash,
"--before", "default_reset",
"--after", "hard_reset",
"write_flash", "-z",
"--flash_mode", "dio",
"--flash_freq", "80m",
"--flash_size", "8MB",
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.boot_app0",
"0x0", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.partitions",
]
elif fw_filename == "rnode_firmware_featheresp32.zip":
if numeric_version >= 1.55:
return [
@@ -2377,6 +2619,24 @@ def main():
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.partitions",
]
elif fw_filename == "rnode_firmware_t3s3.zip":
return [
sys.executable, flasher,
"--chip", "esp32s3",
"--port", args.port,
"--baud", args.baud_flash,
"--before", "default_reset",
"--after", "hard_reset",
"write_flash", "-z",
"--flash_mode", "dio",
"--flash_freq", "80m",
"--flash_size", "4MB",
"0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.boot_app0",
"0x0", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bootloader",
"0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bin",
"0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
"0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.partitions",
]
elif fw_filename == "extracted_rnode_firmware.zip":
return [
sys.executable, flasher,
@@ -2512,6 +2772,7 @@ def main():
if args.eeprom_wipe:
RNS.log("WARNING: EEPROM is being wiped! Power down device NOW if you do not want this!")
rnode.wipe_eeprom()
rnode.hard_reset()
exit()
RNS.log("Reading EEPROM...")
@@ -2704,6 +2965,24 @@ def main():
RNS.log("Setting display intensity to "+str(di))
rnode.set_display_intensity(di)
if isinstance(args.display_addr, str):
set_addr = False
try:
if args.display_addr.startswith("0x"):
args.display_addr = args.display_addr[2:]
da = bytes.fromhex(args.display_addr)
set_addr = True
except Exception as e:
pass
if set_addr and len(da) == 1:
RNS.log("Setting display address to "+RNS.hexrep(da, delimit=False))
rnode.set_display_address(ord(da))
rnode.hard_reset()
exit()
else:
RNS.log("Invalid display address specified")
if args.bluetooth_on:
RNS.log("Enabling Bluetooth...")
rnode.enable_bluetooth()
@@ -2743,6 +3022,7 @@ def main():
RNS.log("\tFirmware version : "+rnode.version)
RNS.log("\tHardware revision : "+str(int(rnode.hw_rev)))
RNS.log("\tSerial number : "+RNS.hexrep(rnode.serialno))
RNS.log("\tModem chip : "+str(models[rnode.model][5]))
RNS.log("\tFrequency range : "+str(rnode.min_freq/1e6)+" MHz - "+str(rnode.max_freq/1e6)+" MHz")
RNS.log("\tMax TX power : "+str(rnode.max_output)+" dBm")
RNS.log("\tManufactured : "+timestring)
@@ -2818,24 +3098,39 @@ def main():
if args.product != None:
if args.product == "03":
mapped_product = ROM.PRODUCT_RNODE
if args.product == "f0":
elif args.product == "f0":
mapped_product = ROM.PRODUCT_HMBRW
if args.product == "e0":
elif args.product == "e0":
mapped_product = ROM.PRODUCT_TBEAM
else:
if len(args.product) == 2:
mapped_product = ord(bytes.fromhex(args.product))
if mapped_model != None:
model = mapped_model
if mapped_model == ROM.MODEL_B4_TCXO:
model = ROM.MODEL_B4
elif mapped_model == ROM.MODEL_B9_TCXO:
model = ROM.MODEL_B9
else:
model = mapped_model
else:
if args.model == "a4":
model = ROM.MODEL_A4
elif args.model == "a9":
model = ROM.MODEL_A9
elif args.model == "a1":
model = ROM.MODEL_A1
elif args.model == "a6":
model = ROM.MODEL_A6
elif args.model == "e4":
model = ROM.MODEL_E4
elif args.model == "e9":
model = ROM.MODEL_E9
elif args.model == "ff":
model = ROM.MODEL_FF
else:
if len(args.model) == 2:
model = ord(bytes.fromhex(args.model))
if args.hwrev != None and (args.hwrev > 0 and args.hwrev < 256):
@@ -2893,6 +3188,10 @@ def main():
RNS.log("No signing key found")
exit()
if model == ROM.MODEL_A1 or model == ROM.MODEL_A6:
rnode.hard_reset()
RNS.log("Waiting for ESP32 reset...")
time.sleep(6.5)
RNS.log("Bootstrapping device EEPROM...")
@@ -2945,6 +3244,8 @@ def main():
partition_hash = get_partition_hash(UPD_DIR+"/"+selected_version+"/"+partition_filename)
if partition_hash != None:
time.sleep(0.75)
RNS.log("Setting firmware checksum...")
rnode.set_firmware_hash(partition_hash)
rnode.hard_reset()
+33 -2
View File
@@ -30,7 +30,7 @@ import argparse
from RNS._version import __version__
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues):
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues, drop_via):
if table:
destination_hash = None
if destination_hexhash != None:
@@ -155,6 +155,29 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
sys.exit(1)
elif drop_via:
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
raise ValueError("Invalid destination entered. Check your input.")
except Exception as e:
print(str(e))
sys.exit(1)
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
if reticulum.drop_all_via(destination_hash):
print("Dropped all paths via "+RNS.prettyhexrep(destination_hash))
else:
print("Unable to drop paths via "+RNS.prettyhexrep(destination_hash)+". Does the transport instance exist?")
sys.exit(1)
else:
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
@@ -256,6 +279,13 @@ def main():
default=False
)
parser.add_argument(
"-x", "--drop-via",
action="store_true",
help="drop all paths via specified transport instance",
default=False
)
parser.add_argument(
"-w",
action="store",
@@ -282,7 +312,7 @@ def main():
else:
configarg = None
if not args.drop_announces and not args.table and not args.rates and not args.destination:
if not args.drop_announces and not args.table and not args.rates and not args.destination and not args.drop_via:
print("")
parser.print_help()
print("")
@@ -296,6 +326,7 @@ def main():
verbosity = args.verbose,
timeout = args.w,
drop_queues = args.drop_announces,
drop_via = args.drop_via,
)
sys.exit(0)
+112 -82
View File
@@ -31,8 +31,10 @@ import argparse
from RNS._version import __version__
DEFAULT_PROBE_SIZE = 16
DEFAULT_TIMEOUT = 12
def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_name = None, verbosity = 0):
def program_setup(configdir, destination_hexhash, size=None, full_name = None, verbosity = 0, timeout=None, wait=0, probes=1):
if size == None: size = DEFAULT_PROBE_SIZE
if full_name == None:
print("The full destination name including application name aspects must be specified for the destination")
exit()
@@ -71,14 +73,19 @@ def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
sys.stdout.flush()
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
i = 0
syms = "⢄⢂⢁⡁⡈⡐⡠"
while not RNS.Transport.has_path(destination_hash):
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
sys.stdout.flush()
i = (i+1)%len(syms)
if time.time() > _timeout:
print("\r \rPath request timed out")
exit(1)
server_identity = RNS.Identity.recall(destination_hash)
request_destination = RNS.Destination(
@@ -89,101 +96,120 @@ def program_setup(configdir, destination_hexhash, size=DEFAULT_PROBE_SIZE, full_
*aspects
)
probe = RNS.Packet(request_destination, os.urandom(size))
receipt = probe.send()
sent = 0
replies = 0
while probes:
if more_output:
more = " via "+RNS.prettyhexrep(reticulum.get_next_hop(destination_hash))+" on "+str(reticulum.get_next_hop_if_name(destination_hash))
else:
more = ""
if sent > 0:
time.sleep(wait)
print("\rSent "+str(size)+" byte probe to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
try:
probe = RNS.Packet(request_destination, os.urandom(size))
probe.pack()
except OSError:
print("Error: Probe packet size of "+str(len(probe.raw))+" bytes exceed MTU of "+str(RNS.Reticulum.MTU)+" bytes")
exit(3)
i = 0
while not receipt.status == RNS.PacketReceipt.DELIVERED:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
sys.stdout.flush()
i = (i+1)%len(syms)
receipt = probe.send()
sent += 1
print("\b\b ")
sys.stdout.flush()
if more_output:
nhd = reticulum.get_next_hop(destination_hash)
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else ""
if_str = " on "+str(reticulum.get_next_hop_if_name(destination_hash)) if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
more = via_str+if_str
else:
more = ""
hops = RNS.Transport.hops_to(destination_hash)
if hops != 1:
ms = "s"
else:
ms = ""
print("\rSent probe "+str(sent)+" ("+str(size)+" bytes) to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
rtt = receipt.get_rtt()
if (rtt >= 1):
rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds"
else:
rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds"
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
i = 0
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
sys.stdout.flush()
i = (i+1)%len(syms)
reception_stats = ""
if reticulum.is_connected_to_shared_instance:
reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash)
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
if time.time() > _timeout:
print("\r \rProbe timed out")
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]"
else:
print("\b\b ")
sys.stdout.flush()
if receipt.status == RNS.PacketReceipt.DELIVERED:
replies += 1
hops = RNS.Transport.hops_to(destination_hash)
if hops != 1:
ms = "s"
else:
ms = ""
rtt = receipt.get_rtt()
if (rtt >= 1):
rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds"
else:
rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds"
reception_stats = ""
if reticulum.is_connected_to_shared_instance:
reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash)
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
reception_q = reticulum.get_packet_q(receipt.proof_packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]"
if reception_q != None:
reception_stats += " [Link Quality "+str(reception_q)+"%]"
else:
if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
print(
"Valid reply from "+
RNS.prettyhexrep(receipt.destination.hash)+
"\nRound-trip time is "+rttstring+
" over "+str(hops)+" hop"+ms+
reception_stats+"\n"
)
else:
print("\r \rProbe timed out")
probes -= 1
loss = round((1-(replies/sent))*100, 2)
print(f"Sent {sent}, received {replies}, packet loss {loss}%")
if loss > 0:
exit(2)
else:
if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
print(
"Valid reply received from "+
RNS.prettyhexrep(receipt.destination.hash)+
"\nRound-trip time is "+rttstring+
" over "+str(hops)+" hop"+ms+
reception_stats
)
exit(0)
def main():
try:
parser = argparse.ArgumentParser(description="Reticulum Probe Utility")
parser.add_argument("--config",
action="store",
default=None,
help="path to alternative Reticulum config directory",
type=str
)
parser.add_argument(
"--version",
action="version",
version="rnprobe {version}".format(version=__version__)
)
parser.add_argument(
"full_name",
nargs="?",
default=None,
help="full destination name in dotted notation",
type=str
)
parser.add_argument(
"destination_hash",
nargs="?",
default=None,
help="hexadecimal hash of the destination",
type=str
)
parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
parser.add_argument("-s", "--size", action="store", default=None, help="size of probe packet payload in bytes", type=int)
parser.add_argument("-n", "--probes", action="store", default=1, help="number of probes to send", type=int)
parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float)
parser.add_argument("-w", "--wait", metavar="seconds", action="store", default=0, help="time between each probe", type=float)
parser.add_argument("--version", action="version", version="rnprobe {version}".format(version=__version__))
parser.add_argument("full_name", nargs="?", default=None, help="full destination name in dotted notation", type=str)
parser.add_argument("destination_hash", nargs="?", default=None, help="hexadecimal hash of the destination", type=str)
parser.add_argument('-v', '--verbose', action='count', default=0)
@@ -202,8 +228,12 @@ def main():
program_setup(
configdir = configarg,
destination_hexhash = args.destination_hash,
size = args.size,
full_name = args.full_name,
verbosity = args.verbose
verbosity = args.verbose,
probes = args.probes,
wait = args.wait,
timeout = args.timeout,
)
except KeyboardInterrupt:
+24 -1
View File
@@ -87,7 +87,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 = False
enable_transport = No
# By default, the first program to launch the Reticulum
@@ -111,6 +111,18 @@ share_instance = Yes
shared_instance_port = 37428
instance_control_port = 37429
# On systems where running instances may not have access
# to the same shared Reticulum configuration directory,
# it is still possible to allow full interactivity for
# running instances, by manually specifying a shared RPC
# key. In almost all cases, this option is not needed, but
# it can be useful on operating systems such as Android.
# The key must be specified as bytes in hexadecimal.
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
# You can configure Reticulum to panic and forcibly close
# if an unrecoverable interface error occurs, such as the
# hardware device for an interface disappearing. This is
@@ -120,6 +132,17 @@ instance_control_port = 37429
panic_on_interface_error = No
# When Transport is enabled, it is possible to allow the
# Transport Instance to respond to probe requests from
# the rnprobe utility. This can be a useful tool to test
# connectivity. When this option is enabled, the probe
# destination will be generated from the Identity of the
# Transport Instance, and printed to the log at startup.
# Optional, and disabled by default.
respond_to_probes = No
[logging]
# Valid log levels are 0 through 7:
# 0: Log only critical information
+96 -26
View File
@@ -46,7 +46,7 @@ def size_str(num, suffix='B'):
return "%.2f%s%s" % (num, last_unit, suffix)
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=False):
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, sorting=None, sort_reverse=False):
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
stats = None
@@ -62,16 +62,38 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
if isinstance(stats[s], bytes):
stats[s] = RNS.hexrep(stats[s], delimit=False)
for i in stats[s]:
if isinstance(i, dict):
for k in i:
if isinstance(i[k], bytes):
i[k] = RNS.hexrep(i[k], delimit=False)
if isinstance(stats[s], dict):
for i in stats[s]:
if isinstance(i, dict):
for k in i:
if isinstance(i[k], bytes):
i[k] = RNS.hexrep(i[k], delimit=False)
print(json.dumps(stats))
exit()
interfaces = stats["interfaces"]
if sorting != None and isinstance(sorting, str):
sorting = sorting.lower()
if sorting == "rate" or sorting == "bitrate":
interfaces.sort(key=lambda i: i["bitrate"], reverse=not sort_reverse)
if sorting == "rx":
interfaces.sort(key=lambda i: i["rxb"], reverse=not sort_reverse)
if sorting == "tx":
interfaces.sort(key=lambda i: i["txb"], reverse=not sort_reverse)
if sorting == "traffic":
interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse)
if sorting == "announces" or sorting == "announce":
interfaces.sort(key=lambda i: i["incoming_announce_frequency"]+i["outgoing_announce_frequency"], reverse=not sort_reverse)
if sorting == "arx":
interfaces.sort(key=lambda i: i["incoming_announce_frequency"], reverse=not sort_reverse)
if sorting == "atx":
interfaces.sort(key=lambda i: i["outgoing_announce_frequency"], reverse=not sort_reverse)
if sorting == "held":
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
for ifstat in stats["interfaces"]:
for ifstat in interfaces:
name = ifstat["name"]
if dispall or not (
@@ -113,7 +135,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
else:
spec_str = " programs"
clients_string = "Serving : "+str(cnum)+spec_str
clients_string = "Serving : "+str(cnum)+spec_str
elif name.startswith("I2PInterface["):
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
cnum = clients
@@ -122,11 +144,11 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
else:
spec_str = " connected I2P endpoints"
clients_string = "Peers : "+str(cnum)+spec_str
clients_string = "Peers : "+str(cnum)+spec_str
else:
clients_string = ""
else:
clients_string = "Clients : "+str(clients)
clients_string = "Clients : "+str(clients)
else:
clients = None
@@ -134,49 +156,63 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None,json=F
print(" {n}".format(n=ifstat["name"]))
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
print(" Status : {ss}".format(ss=ss))
print(" Status : {ss}".format(ss=ss))
if clients != None and clients_string != "":
print(" "+clients_string)
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
print(" Mode : {mode}".format(mode=modestr))
print(" Mode : {mode}".format(mode=modestr))
if "bitrate" in ifstat and ifstat["bitrate"] != None:
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
if "airtime_short" in ifstat and "airtime_long" in ifstat:
print(" Airtime : {atl}% (1h), {ats}% (15s)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
print(" Ch.Load : {atl}% (1h), {ats}% (15s)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
if "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
if astats and "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
aqn = ifstat["announce_queue"]
if aqn == 1:
print(" Queued : {np} announce".format(np=aqn))
print(" Queued : {np} announce".format(np=aqn))
else:
print(" Queued : {np} announces".format(np=aqn))
print(" Queued : {np} announces".format(np=aqn))
print(" Traffic : {txb}\n {rxb}".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
aqn = ifstat["held_announces"]
if aqn == 1:
print(" Held : {np} announce".format(np=aqn))
else:
print(" Held : {np} announces".format(np=aqn))
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
print(" Announces : {iaf}".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
print(" {iaf}".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
print(" Traffic : {txb}\n {rxb}".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
if "transport_id" in stats and stats["transport_id"] != None:
print("\n Reticulum Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running, uptime is "+RNS.prettytime(stats["transport_uptime"]))
print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
if "probe_responder" in stats and stats["probe_responder"] != None:
print(" Probe responder at "+RNS.prettyhexrep(stats["probe_responder"])+ " active")
print(" Uptime is "+RNS.prettytime(stats["transport_uptime"]))
print("")
@@ -197,6 +233,31 @@ def main():
default=False
)
parser.add_argument(
"-A",
"--announce-stats",
action="store_true",
help="show announce stats",
default=False
)
parser.add_argument(
"-s",
"--sort",
action="store",
help="sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]",
default=None,
type=str
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
help="reverse sorting",
default=False,
)
parser.add_argument(
"-j",
"--json",
@@ -216,7 +277,16 @@ def main():
else:
configarg = None
program_setup(configdir = configarg, dispall = args.all, verbosity=args.verbose, name_filter=args.filter, json=args.json)
program_setup(
configdir = configarg,
dispall = args.all,
verbosity=args.verbose,
name_filter=args.filter,
json=args.json,
astats=args.announce_stats,
sorting=args.sort,
sort_reverse=args.reverse,
)
except KeyboardInterrupt:
print("")
+53 -7
View File
@@ -1,6 +1,6 @@
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,7 @@ from .Transport import Transport
from .Destination import Destination
from .Packet import Packet
from .Packet import PacketReceipt
from .Resolver import Resolver
from .Resource import Resource, ResourceAdvertisement
from .Cryptography import HKDF
from .Cryptography import Hashes
@@ -143,6 +144,12 @@ def rand():
result = instance_random.random()
return result
def trace_exception(e):
import traceback
exception_info = "".join(traceback.TracebackException.from_exception(e).format())
log(f"An unhandled {str(type(e))} exception occurred: {str(e)}", LOG_ERROR)
log(exception_info, LOG_ERROR)
def hexrep(data, delimit=True):
try:
iter(data)
@@ -160,6 +167,9 @@ def prettyhexrep(data):
hexrep = "<"+delimiter.join("{:02x}".format(c) for c in data)+">"
return hexrep
def prettyspeed(num, suffix="b"):
return prettysize(num/8, suffix=suffix)+"ps"
def prettysize(num, suffix='B'):
units = ['','K','M','G','T','P','E','Z']
last_unit = 'Y'
@@ -179,32 +189,68 @@ def prettysize(num, suffix='B'):
return "%.2f%s%s" % (num, last_unit, suffix)
def prettytime(time, verbose=False):
def prettyfrequency(hz, suffix="Hz"):
num = hz*1e6
units = ["µ", "m", "", "K","M","G","T","P","E","Z"]
last_unit = "Y"
for unit in units:
if abs(num) < 1000.0:
return "%.2f %s%s" % (num, unit, suffix)
num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix)
def prettydistance(m, suffix="m"):
num = m*1e6
units = ["µ", "m", "c", ""]
last_unit = "K"
for unit in units:
divisor = 1000.0
if unit == "m": divisor = 10
if unit == "c": divisor = 100
if abs(num) < divisor:
return "%.2f %s%s" % (num, unit, suffix)
num /= divisor
return "%.2f %s%s" % (num, last_unit, suffix)
def prettytime(time, verbose=False, compact=False):
days = int(time // (24 * 3600))
time = time % (24 * 3600)
hours = int(time // 3600)
time %= 3600
minutes = int(time // 60)
time %= 60
seconds = round(time, 2)
if compact:
seconds = int(time)
else:
seconds = round(time, 2)
ss = "" if seconds == 1 else "s"
sm = "" if minutes == 1 else "s"
sh = "" if hours == 1 else "s"
sd = "" if days == 1 else "s"
displayed = 0
components = []
if days > 0:
if days > 0 and ((not compact) or displayed < 2):
components.append(str(days)+" day"+sd if verbose else str(days)+"d")
displayed += 1
if hours > 0:
if hours > 0 and ((not compact) or displayed < 2):
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h")
displayed += 1
if minutes > 0:
if minutes > 0 and ((not compact) or displayed < 2):
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m")
displayed += 1
if seconds > 0:
if seconds > 0 and ((not compact) or displayed < 2):
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
displayed += 1
i = 0
tstr = ""
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.5.9"
__version__ = "0.7.4"
+10 -10
View File
@@ -14,14 +14,14 @@ This document outlines the currently established development roadmap for Reticul
## Currently Active Work Areas
For each release cycle of Reticulum, improvements and additions from the five [Primary Efforts](#primary-efforts) are selected as active work areas, and can be expected to be included in the upcoming releases within that cycle. While not entirely set in stone for each release cycle, they serve as a pointer of what to expect in the near future.
- The current `0.5.x` release cycle aims at completing
- [x] Reach feature-completion of the Reticulum API
- [x] Improve performance and efficiency of the `Buffer` and `Channel` API
- [x] Add bluetooth pairing code output to rnodeconf
- The current `0.7.x` release cycle aims at completing
- [ ] Overhauling and updating the documentation
- [ ] Performance and memory optimisations of the Python reference implementation
- [ ] Fixing potential bugs
- [ ] Distributed Destination Naming System
- [ ] Create a standalone RNS Daemon app for Android
- [ ] Network-wide path balancing
- [ ] Add automatic retries to all use cases of the `Request` API
- [ ] Performance and memory optimisations of the Python reference implementation
- [ ] Fixing bugs discovered while operating Reticulum systems and applications
## Primary Efforts
The development path for Reticulum is currently laid out in five distinct areas: *Comprehensibility*, *Universality*, *Functionality*, *Usability & Utility* and *Interfaceability*. Conceptualising the development of Reticulum into these areas serves to advance the implementation and work towards the Foundational Goals & Values of Reticulum.
@@ -40,12 +40,12 @@ These efforts are aimed at improving the ease of which Reticulum is understood,
- Update NomadNet screenshots
- Update Sideband screenshots
- Installation
- Add a *Reticulum On Raspberry Pi* section
- Update *Reticulum On Android* section if necessary
- Update Android install documentation.
- [x] Add a *Reticulum On Raspberry Pi* section
- [x] Update *Reticulum On Android* section if necessary
- [x] Update Android install documentation.
- Communications hardware section
- Add information about RNode external displays.
- Packet radio modems.
- [x] Packet radio modems.
- Possibly add other relevant types here as well.
- Setup *Best Practices For...* / *Installation Examples* section.
- Home or office (example)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 9ef972bb4338b710c7167c93f93f8e8a
config: 541171817b95d649201365804faf729c
tags: 645f666f9bcd5a90fca523b33c5a78b7
+11 -11
View File
@@ -180,29 +180,29 @@ and :ref:`Interfaces<interfaces-main>` chapters of this manual.
Connecting Reticulum Instances Over the Internet
================================================
Reticulum currently offers two interfaces suitable for connecting instances over the Internet: :ref:`TCP<interfaces-tcps>`
and :ref:`I2P<interfaces-i2p>`. Each interface offers a different set of features, and Reticulum
users should carefully choose the interface which best suites their needs.
and :ref:`I2P<interfaces-i2p>`. Each interface offers a different set of features, and Reticulum
users should carefully choose the interface which best suites their needs.
The ``TCPServerInterface`` allows users to host an instance accessible over TCP/IP. This
method is generally faster, lower latency, and more energy efficient than using ``I2PInterface``,
however it also leaks more data about the server host.
TCP connections reveal the IP address of both your instance and the server to anyone who can
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
inspecting your packets may be able to record packet metadata like time of transmission and packet size.
Even though Reticulum encrypts traffic, TCP does not, so an adversary may be able to use
packet inspection to learn that a system is running Reticulum, and what other IP addresses connect to it.
Hosting a publicly reachable instance over TCP also requires a publicly reachable IP address,
which most Internet connections don't offer anymore.
The ``I2PInterface`` routes messages through the `Invisible Internet Protocol
The ``I2PInterface`` routes messages through the `Invisible Internet Protocol
(I2P) <https://geti2p.net/en/>`_. To use this interface, users must also run an I2P daemon in
parallel to ``rnsd``. For always-on I2P nodes it is recommended to use `i2pd <https://i2pd.website/>`_.
parallel to ``rnsd``. For always-on I2P nodes it is recommended to use `i2pd <https://i2pd.website/>`_.
By default, I2P will encrypt and mix all traffic sent over the Internet, and
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
By default, I2P will encrypt and mix all traffic sent over the Internet, and
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
will also relay other I2P user's encrypted packets, which will use extra
bandwidth and compute power, but also makes timing attacks and other forms of
bandwidth and compute power, but also makes timing attacks and other forms of
deep-packet-inspection much more difficult.
I2P also allows users to host globally available Reticulum instances from non-public IP's and behind firewalls and NAT.
@@ -415,7 +415,7 @@ locally on your device using the following command:
It is also possible to include Reticulum in apps compiled and distributed as
Android APKs. A detailed tutorial and example source code will be included
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and startig point.
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and starting point.
ARM64
@@ -455,7 +455,7 @@ for including and using Reticulum in your own scripts and programs.
.. code::
# Install pipx
# Install pipx
sudo apt install pipx
# Make installed programs available on the command line
@@ -489,7 +489,7 @@ for including and using Reticulum in your own scripts and programs.
.. code::
# Install pipx
# Install pipx
sudo apt install pipx
# Make installed programs available on the command line
+94 -6
View File
@@ -1,9 +1,9 @@
.. _interfaces-main:
********************
Supported Interfaces
********************
**********************
Configuring Interfaces
**********************
Reticulum supports using many kinds of devices as networking interfaces, and
allows you to mix and match them in any way you choose. The number of distinct
@@ -33,9 +33,20 @@ system, which should be enabled by default in almost all OSes.
.. code::
# This example demonstrates a TCP server interface.
# It will listen for incoming connections on the
# specified IP address and port number.
# This example demonstrates a bare-minimum setup
# of an Auto Interface. It will allow communica-
# tion with all other reachable devices on all
# usable physical ethernet-based devices that
# are available on the system.
[[Default Interface]]
type = AutoInterface
interface_enabled = True
# This example demonstrates an more specifically
# configured Auto Interface, that only uses spe-
# cific physical interfaces, and has a number of
# other configuration options set.
[[Default Interface]]
type = AutoInterface
@@ -47,6 +58,12 @@ system, which should be enabled by default in almost all OSes.
group_id = reticulum
# You can also choose the multicast address type:
# temporary (default, Temporary Multicast Address)
# or permanent (Permanent Multicast Address)
multicast_address_type = permanent
# You can also select specifically which
# kernel networking devices to use.
@@ -761,3 +778,74 @@ conserve bandwidth, while very fast networks can support applications that
need very frequent announces. Reticulum implements these mechanisms to ensure
that a large span of network types can seamlessly *co-exist* and interconnect.
.. _interfaces-ingress-control:
New Destination Rate Limiting
=============================
On public interfaces, where anyone may connect and announce new destinations,
it can be useful to control the rate at which announces for *new* destinations are
processed.
If a large influx of announces for newly created or previously unknown destinations
occur within a short amount of time, Reticulum will place these announces on hold,
so that announce traffic for known and previously established destinations can
continue to be processed without interruptions.
After the burst subsides, and an additional waiting period has passed, the held
announces will be released at a slow rate, until the hold queue is cleared. This
also means, that should a node decide to connect to a public interface, announce
a large amount of bogus destinations, and then disconnect, these destination will
never make it into path tables and waste network bandwidth on retransmitted
announces.
**It's important to note** that the ingress control works at the level of *individual
sub-interfaces*. As an example, this means that one client on a :ref:`TCP Server Interface<interfaces-tcps>`
cannot disrupt processing of incoming announces for other connected clients on the same
:ref:`TCP Server Interface<interfaces-tcps>`. All other clients on the same interface will still have new announces
processed without interruption.
By default, Reticulum will handle this automatically, and ingress announce
control will be enabled on interface where it is sensible to do so. It should
generally not be neccessary to modify the ingress control configuration,
but all the parameters are exposed for configuration if needed.
* | The ``ingress_control`` option tells Reticulum whether or not
to enable announce ingress control on the interface. Defaults to
``True``.
* | The ``ic_new_time`` option configures how long (in seconds) an
interface is considered newly spawned. Defaults to ``2*60*60`` seconds. This
option is useful on publicly accessible interfaces that spawn new
sub-interfaces when a new client connects.
* | The ``ic_burst_freq_new`` option sets the maximum announce ingress
frequency for newly spawned interfaces. Defaults to ``3.5``
announces per second.
* | The ``ic_burst_freq`` option sets the maximum announce ingress
frequency for other interfaces. Defaults to ``12`` announces
per second.
*If an interface exceeds its burst frequency, incoming announces
for unknown destinations will be temporarily held in a queue, and
not processed until later.*
* | The ``ic_max_held_announces`` option sets the maximum amount of
unique announces that will be held in the queue. Any additional
unique announces will be dropped. Defaults to ``256`` announces.
* | The ``ic_burst_hold`` option sets how much time (in seconds) must
pass after the burst frequency drops below its threshold, for the
announce burst to be considered cleared. Defaults to ``60``
seconds.
* | The ``ic_burst_penalty`` option sets how much time (in seconds) must
pass after the burst is considered cleared, before held announces can
start being released from the queue. Defaults to ``5*60``
seconds.
* | The ``ic_held_release_interval`` option sets how much time (in seconds)
must pass between releasing each held announce from the queue. Defaults
to ``30`` seconds.
+2 -2
View File
@@ -75,7 +75,7 @@ guide the design of Reticulum:
it can be easily modified and replicated by anyone interested in doing so.
* **Very low bandwidth requirements**
Reticulum should be able to function reliably over links with a transmission capacity as low
as *500 bits per second*.
as *5 bits per second*.
* **Encryption by default**
Reticulum must use strong encryption by default for all communication.
* **Initiator Anonymity**
@@ -595,7 +595,7 @@ or less any medium that allows you to send and receive data, which satisfies som
minimum requirements.
The communication channel must support at least half-duplex operation, and provide an average
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes. The
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
runtime environment.
+128 -11
View File
@@ -99,6 +99,17 @@ configuration file is created. The default configuration looks like this:
instance_control_port = 37429
# On systems where running instances may not have access
# to the same shared Reticulum configuration directory,
# it is still possible to allow full interactivity for
# running instances, by manually specifying a shared RPC
# key. In almost all cases, this option is not needed, but
# it can be useful on operating systems such as Android.
# The key must be specified as bytes in hexadecimal.
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
# You can configure Reticulum to panic and forcibly close
# if an unrecoverable interface error occurs, such as the
# hardware device for an interface disappearing. This is
@@ -108,6 +119,17 @@ configuration file is created. The default configuration looks like this:
panic_on_interface_error = No
# When Transport is enabled, it is possible to allow the
# Transport Instance to respond to probe requests from
# the rnprobe utility. This can be a useful tool to test
# connectivity. When this option is enabled, the probe
# destination will be generated from the Identity of the
# Transport Instance, and printed to the log at startup.
# Optional, and disabled by default.
respond_to_probes = No
[logging]
# Valid log levels are 0 through 7:
# 0: Log only critical information
@@ -192,6 +214,19 @@ Run ``rnsd``:
[2023-08-18 17:59:56] [Notice] Started rnsd version 0.5.8
Run ``rnsd`` in service mode, ensuring all logging output is sent directly to file:
.. code:: text
$ rnsd -s
Generate a verbose and detailed configuration example, with explanations of all the
various configuration options, and interface configuration examples:
.. code:: text
$ rnsd --exampleconfig
**All Command-Line Options**
.. code:: text
@@ -277,19 +312,23 @@ Filter output to only show some interfaces:
.. code:: text
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-j] [-v] [filter]
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
[-r] [-j] [-v] [filter]
Reticulum Network Stack Status
positional arguments:
filter only display interfaces with names including filter
filter only display interfaces with names including filter
options:
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
--version show program's version number and exit
-a, --all show all interfaces
-j, --json output in JSON format
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
--version show program's version number and exit
-a, --all show all interfaces
-A, --announce-stats show announce stats
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
-r, --reverse reverse sorting
-j, --json output in JSON format
-v, --verbose
@@ -414,7 +453,7 @@ Resolve path to a destination:
.. code:: text
usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
[-w seconds] [-v] [destination]
[-x] [-w seconds] [-v] [destination]
Reticulum Path Discovery Utility
@@ -429,6 +468,7 @@ Resolve path to a destination:
-r, --rates show announce rate info
-d, --drop remove the path to a destination
-D, --drop-announces drop all queued announces
-x, --drop-via drop all paths via specified transport instance
-w seconds timeout before giving up
-v, --verbose
@@ -439,7 +479,12 @@ The rnprobe Utility
The ``rnprobe`` utility lets you probe a destination for connectivity, similar
to the ``ping`` program. Please note that probes will only be answered if the
specified destination is configured to send proofs for received packets. Many
destinations will not have this option enabled, and will not be probable.
destinations will not have this option enabled, so most destinations will not
be probable.
You can enable a probe-reply destination on Reticulum Transport Instances by
setting the ``respond_to_probes`` configuration directive. Reticulum will then
print the probe destination to the log on Transport Instance startup.
**Usage Examples**
@@ -447,17 +492,40 @@ Probe a destination:
.. code:: text
$ rnprobe example_utilities.echo.request 2d03725b327348980d570f739a3a5708
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
Valid reply received from <2d03725b327348980d570f739a3a5708>
Round-trip time is 38.469 milliseconds over 2 hops
Send a larger probe:
.. code:: text
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708 -s 256
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
Valid reply received from <2d03725b327348980d570f739a3a5708>
Round-trip time is 38.781 milliseconds over 2 hops
If the interface that receives the probe replies supports reporting radio
parameters such as **RSSI** and **SNR**, the ``rnprobe`` utility will print
these as part of the result as well.
.. code:: text
$ rnprobe rnstransport.probe e7536ee90bd4a440e130490b87a25124
Sent 16 byte probe to <e7536ee90bd4a440e130490b87a25124>
Valid reply received from <e7536ee90bd4a440e130490b87a25124>
Round-trip time is 1.809 seconds over 1 hop [RSSI -73 dBm] [SNR 12.0 dB]
**All Command-Line Options**
.. code:: text
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [-s SIZE]
[full_name] [destination_hash]
Reticulum Probe Utility
@@ -468,6 +536,7 @@ Probe a destination:
optional arguments:
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
-s SIZE, --size SIZE size of probe packet payload in bytes
--version show program's version number and exit
-v, --verbose
@@ -711,6 +780,9 @@ Reticulum as a System Service
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
service and have it start automatically at boot.
Systemwide Service
^^^^^^^^^^^^^^^^^^
If you installed Reticulum with ``pip``, the ``rnsd`` program will most likely
be located in a user-local installation path only, which means ``systemd`` will not
be able to execute it. In this case, you can simply symlink the ``rnsd`` program
@@ -758,3 +830,48 @@ If you want to automatically start ``rnsd`` at boot, run:
.. code:: text
sudo systemctl enable rnsd
Userspace Service
^^^^^^^^^^^^^^^^^
Alternatively you can use a user systemd service instead of a system wide one. This way the whole setup can be done as a regular user.
Create a user systemd service files ``~/.config/systemd/user/rnsd.service`` with the following content:
.. code:: text
[Unit]
Description=Reticulum Network Stack Daemon
After=default.target
[Service]
# If you run Reticulum on WiFi devices,
# or other devices that need some extra
# time to initialise, you might want to
# add a short delay before Reticulum is
# started by systemd:
# ExecStartPre=/bin/sleep 10
Type=simple
Restart=always
RestartSec=3
ExecStart=RNS_BIN_DIR/rnsd --service
[Install]
WantedBy=default.target
Replace ``RNS_BIN_DIR`` with the path to your Reticulum binary directory (eg. /home/USERNAMEHERE/rns/bin).
Start user service:
.. code:: text
systemctl --user daemon-reload
systemctl --user start rnsd.service
If you want to automatically start ``rnsd`` without having to log in as the USERNAMEHERE, do:
.. code:: text
sudo loginctl enable-linger USERNAMEHERE
systemctl --user enable rnsd.service
+1 -1
View File
@@ -91,7 +91,7 @@ What does Reticulum Offer?
Where can Reticulum be Used?
============================
Over practically any medium that can support at least a half-duplex channel
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
ad-hoc WiFi, free-space optical links and similar systems are all examples
of the types of interfaces Reticulum was designed for.
@@ -0,0 +1,134 @@
/*
* _sphinx_javascript_frameworks_compat.js
* ~~~~~~~~~~
*
* Compatability shim for jQuery and underscores.js.
*
* WILL BE REMOVED IN Sphinx 6.0
* xref RemovedInSphinx60Warning
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}
+1 -22
View File
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -324,7 +324,6 @@ aside.sidebar {
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
@@ -332,7 +331,6 @@ div.admonition, div.topic, blockquote {
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
@@ -608,7 +606,6 @@ ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
@@ -670,16 +667,6 @@ dd {
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
@@ -748,14 +735,6 @@ abbr, acronym {
cursor: help;
}
.translated {
background-color: rgba(207, 255, 207, 0.2)
}
.untranslated {
background-color: rgba(255, 207, 207, 0.2)
}
/* -- code displays --------------------------------------------------------- */
pre {
+1 -1
View File
@@ -4,7 +4,7 @@
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
+1 -1
View File
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.5.9 beta',
VERSION: '0.7.4 beta',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
+10881
View File
File diff suppressed because it is too large Load Diff
+2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
+1 -1
View File
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+52 -26
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Code Examples - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Code Examples - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -258,7 +258,7 @@ program.</p>
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
<span class="c1"># and communicated with. Destinations can also announce their</span>
<span class="c1"># existence, which will let the network know they are reachable</span>
<span class="c1"># and autoomatically create paths to them, from anywhere else</span>
<span class="c1"># and automatically create paths to them, from anywhere else</span>
<span class="c1"># in the network.</span>
<span class="n">destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">identity</span><span class="p">,</span>
@@ -269,7 +269,7 @@ program.</p>
<span class="p">)</span>
<span class="c1"># We configure the destination to automatically prove all</span>
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
<span class="c1"># tries to communicate with the destination know whether their</span>
@@ -375,7 +375,7 @@ notifications about announces from relevant destinations.</p>
<span class="c1"># Destinations are endpoints in Reticulum, that can be addressed</span>
<span class="c1"># and communicated with. Destinations can also announce their</span>
<span class="c1"># existence, which will let the network know they are reachable</span>
<span class="c1"># and autoomatically create paths to them, from anywhere else</span>
<span class="c1"># and automatically create paths to them, from anywhere else</span>
<span class="c1"># in the network.</span>
<span class="n">destination_1</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">identity</span><span class="p">,</span>
@@ -396,7 +396,7 @@ notifications about announces from relevant destinations.</p>
<span class="p">)</span>
<span class="c1"># We configure the destinations to automatically prove all</span>
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
<span class="c1"># back to the sender of that packet. This will let anyone that</span>
<span class="c1"># tries to communicate with the destination know whether their</span>
@@ -656,6 +656,7 @@ the Packet interface.</p>
<span class="c1"># of the packet. #</span>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">RNS</span>
@@ -678,8 +679,19 @@ the Packet interface.</p>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Randomly create a new identity for our echo server</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="c1"># Load identity from file if it exist or randomley create</span>
<span class="k">if</span> <span class="n">configpath</span><span class="p">:</span>
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">/storage/identitiesy/</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">configpath</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">/storage/identities/</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">configdir</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">):</span>
<span class="c1"># Load identity from file</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">from_file</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;loaded identity from file: &quot;</span><span class="o">+</span><span class="n">ifilepath</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Randomly create a new identity for our echo example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;created new identity&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
<span class="c1"># We create a destination that clients can query. We want</span>
<span class="c1"># to be able to verify echo replies to our clients, so we</span>
@@ -697,7 +709,7 @@ the Packet interface.</p>
<span class="p">)</span>
<span class="c1"># We configure the destination to automatically prove all</span>
<span class="c1"># packets adressed to it. By doing this, RNS will automatically</span>
<span class="c1"># packets addressed to it. By doing this, RNS will automatically</span>
<span class="c1"># generate a proof for each incoming packet and transmit it</span>
<span class="c1"># back to the sender of that packet.</span>
<span class="n">echo_destination</span><span class="o">.</span><span class="n">set_proof_strategy</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">PROVE_ALL</span><span class="p">)</span>
@@ -1018,8 +1030,20 @@ destination, and passing traffic back and forth over the link.</p>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Randomly create a new identity for our link example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="c1"># Load identity from file if it exist or randomley create</span>
<span class="k">if</span> <span class="n">configpath</span><span class="p">:</span>
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">/storage/identitiesy/</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">configpath</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">/storage/identities/</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">configdir</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;ifilepath: </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">ifilepath</span><span class="p">)</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">):</span>
<span class="c1"># Load identity from file</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">from_file</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;loaded identity from file: &quot;</span><span class="o">+</span><span class="n">ifilepath</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Randomly create a new identity for our link example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;created new identity&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
<span class="c1"># We create a destination that clients can connect to. We</span>
<span class="c1"># want clients to create links to this destination, so we</span>
@@ -3084,8 +3108,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="c1"># And set up a small job to check for</span>
<span class="c1"># a potential timeout in receiving the</span>
<span class="c1"># file list</span>
<span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">filelist_timeout_job</span><span class="p">)</span>
<span class="n">thread</span><span class="o">.</span><span class="n">setDaemon</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span>
<span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">filelist_timeout_job</span><span class="p">,</span> <span class="n">daemon</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="c1"># This job just sleeps for the specified</span>
@@ -3322,11 +3345,14 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+18 -15
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -257,11 +257,14 @@
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+32 -15
View File
@@ -4,12 +4,12 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -139,7 +139,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -159,13 +159,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -179,7 +179,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -373,6 +373,8 @@
<li><a href="reference.html#RNS.Link.get_establishment_rate">get_establishment_rate() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Resource.get_hash">get_hash() (RNS.Resource method)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.get_instance">get_instance() (RNS.Reticulum static method)</a>
</li>
<li><a href="reference.html#RNS.Resource.get_parts">get_parts() (RNS.Resource method)</a>
</li>
@@ -389,6 +391,8 @@
</li>
</ul></li>
<li><a href="reference.html#RNS.Identity.get_public_key">get_public_key() (RNS.Identity method)</a>
</li>
<li><a href="reference.html#RNS.Link.get_q">get_q() (RNS.Link method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
@@ -401,10 +405,14 @@
<li><a href="reference.html#RNS.RequestReceipt.get_response">get_response() (RNS.RequestReceipt method)</a>
</li>
<li><a href="reference.html#RNS.RequestReceipt.get_response_time">get_response_time() (RNS.RequestReceipt method)</a>
</li>
<li><a href="reference.html#RNS.Link.get_rssi">get_rssi() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
</li>
<li><a href="reference.html#RNS.Resource.get_segments">get_segments() (RNS.Resource method)</a>
</li>
<li><a href="reference.html#RNS.Link.get_snr">get_snr() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
@@ -502,6 +510,8 @@
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Reticulum.MINIMUM_BITRATE">MINIMUM_BITRATE (RNS.Reticulum attribute)</a>
</li>
<li><a href="reference.html#RNS.MessageBase.MSGTYPE">MSGTYPE (RNS.MessageBase attribute)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.MTU">MTU (RNS.Reticulum attribute)</a>
@@ -520,6 +530,8 @@
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link.no_data_for">no_data_for() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.no_inbound_for">no_inbound_for() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.no_outbound_for">no_outbound_for() (RNS.Link method)</a>
@@ -656,10 +668,12 @@
</li>
<li><a href="reference.html#RNS.Identity.to_file">to_file() (RNS.Identity method)</a>
</li>
<li><a href="reference.html#RNS.Transport">Transport (class in RNS)</a>
<li><a href="reference.html#RNS.Link.track_phy_stats">track_phy_stats() (RNS.Link method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Transport">Transport (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.transport_enabled">transport_enabled() (RNS.Reticulum static method)</a>
</li>
<li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a>
@@ -723,11 +737,14 @@
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+19 -16
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Getting Started Fast - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Getting Started Fast - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -560,7 +560,7 @@ locally on your device using the following command:</p>
</div>
<p>It is also possible to include Reticulum in apps compiled and distributed as
Android APKs. A detailed tutorial and example source code will be included
here at a later point. Until then you can use the <a class="reference external" href="https://github.com/markqvist/sideband">Sideband source code</a> as an example and startig point.</p>
here at a later point. Until then you can use the <a class="reference external" href="https://github.com/markqvist/sideband">Sideband source code</a> as an example and starting point.</p>
</section>
<section id="arm64">
<h3>ARM64<a class="headerlink" href="#arm64" title="Permalink to this heading">#</a></h3>
@@ -758,11 +758,14 @@ section of this manual.</p>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+20 -17
View File
@@ -3,15 +3,15 @@
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Supported Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Communications Hardware - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Communications Hardware - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -441,7 +441,7 @@ can be used with Reticulum. This includes virtual software modems such as
<div class="context">
<span>Next</span>
</div>
<div class="title">Supported Interfaces</div>
<div class="title">Configuring Interfaces</div>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
@@ -519,11 +519,14 @@ can be used with Reticulum. This includes virtual software modems such as
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+20 -16
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="#"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="#">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -330,7 +330,7 @@ to participate in the development of Reticulum itself.</p>
<li class="toctree-l2"><a class="reference internal" href="hardware.html#packet-radio-modems">Packet Radio Modems</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a><ul>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a><ul>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#auto-interface">Auto Interface</a></li>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#i2p-interface">I2P Interface</a></li>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#tcp-server-interface">TCP Server Interface</a></li>
@@ -344,6 +344,7 @@ to participate in the development of Reticulum itself.</p>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#common-interface-options">Common Interface Options</a></li>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#interface-modes">Interface Modes</a></li>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#announce-rate-control">Announce Rate Control</a></li>
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#new-destination-rate-limiting">New Destination Rate Limiting</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a><ul>
@@ -467,11 +468,14 @@ to participate in the development of Reticulum itself.</p>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+127 -21
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Supported Interfaces - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Configuring Interfaces - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Supported Interfaces</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -221,8 +221,8 @@
</label>
</div>
<article role="main">
<section id="supported-interfaces">
<span id="interfaces-main"></span><h1>Supported Interfaces<a class="headerlink" href="#supported-interfaces" title="Permalink to this heading">#</a></h1>
<section id="configuring-interfaces">
<span id="interfaces-main"></span><h1>Configuring Interfaces<a class="headerlink" href="#configuring-interfaces" title="Permalink to this heading">#</a></h1>
<p>Reticulum supports using many kinds of devices as networking interfaces, and
allows you to mix and match them in any way you choose. The number of distinct
network topologies you can create with Reticulum is more or less endless, but
@@ -241,9 +241,20 @@ infrastructure like routers or DHCP servers, but will require at least some
sort of switching medium between peers (a wired switch, a hub, a WiFi access
point or similar), and that link-local IPv6 is enabled in your operating
system, which should be enabled by default in almost all OSes.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example demonstrates a TCP server interface.</span>
<span class="c1"># It will listen for incoming connections on the</span>
<span class="c1"># specified IP address and port number.</span>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example demonstrates a bare-minimum setup</span>
<span class="c1"># of an Auto Interface. It will allow communica-</span>
<span class="c1"># tion with all other reachable devices on all</span>
<span class="c1"># usable physical ethernet-based devices that</span>
<span class="c1"># are available on the system.</span>
<span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># This example demonstrates an more specifically</span>
<span class="c1"># configured Auto Interface, that only uses spe-</span>
<span class="c1"># cific physical interfaces, and has a number of</span>
<span class="c1"># other configuration options set.</span>
<span class="p">[[</span><span class="n">Default</span> <span class="n">Interface</span><span class="p">]]</span>
<span class="nb">type</span> <span class="o">=</span> <span class="n">AutoInterface</span>
@@ -255,6 +266,12 @@ system, which should be enabled by default in almost all OSes.</p>
<span class="n">group_id</span> <span class="o">=</span> <span class="n">reticulum</span>
<span class="c1"># You can also choose the multicast address type:</span>
<span class="c1"># temporary (default, Temporary Multicast Address)</span>
<span class="c1"># or permanent (Permanent Multicast Address)</span>
<span class="n">multicast_address_type</span> <span class="o">=</span> <span class="n">permanent</span>
<span class="c1"># You can also select specifically which</span>
<span class="c1"># kernel networking devices to use.</span>
@@ -937,6 +954,91 @@ conserve bandwidth, while very fast networks can support applications that
need very frequent announces. Reticulum implements these mechanisms to ensure
that a large span of network types can seamlessly <em>co-exist</em> and interconnect.</p>
</section>
<section id="new-destination-rate-limiting">
<span id="interfaces-ingress-control"></span><h2>New Destination Rate Limiting<a class="headerlink" href="#new-destination-rate-limiting" title="Permalink to this heading">#</a></h2>
<p>On public interfaces, where anyone may connect and announce new destinations,
it can be useful to control the rate at which announces for <em>new</em> destinations are
processed.</p>
<p>If a large influx of announces for newly created or previously unknown destinations
occur within a short amount of time, Reticulum will place these announces on hold,
so that announce traffic for known and previously established destinations can
continue to be processed without interruptions.</p>
<p>After the burst subsides, and an additional waiting period has passed, the held
announces will be released at a slow rate, until the hold queue is cleared. This
also means, that should a node decide to connect to a public interface, announce
a large amount of bogus destinations, and then disconnect, these destination will
never make it into path tables and waste network bandwidth on retransmitted
announces.</p>
<p><strong>Its important to note</strong> that the ingress control works at the level of <em>individual
sub-interfaces</em>. As an example, this means that one client on a <a class="reference internal" href="#interfaces-tcps"><span class="std std-ref">TCP Server Interface</span></a>
cannot disrupt processing of incoming announces for other connected clients on the same
<a class="reference internal" href="#interfaces-tcps"><span class="std std-ref">TCP Server Interface</span></a>. All other clients on the same interface will still have new announces
processed without interruption.</p>
<p>By default, Reticulum will handle this automatically, and ingress announce
control will be enabled on interface where it is sensible to do so. It should
generally not be neccessary to modify the ingress control configuration,
but all the parameters are exposed for configuration if needed.</p>
<blockquote>
<div><ul>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ingress_control</span></code> option tells Reticulum whether or not
to enable announce ingress control on the interface. Defaults to
<code class="docutils literal notranslate"><span class="pre">True</span></code>.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_new_time</span></code> option configures how long (in seconds) an
interface is considered newly spawned. Defaults to <code class="docutils literal notranslate"><span class="pre">2*60*60</span></code> seconds. This
option is useful on publicly accessible interfaces that spawn new
sub-interfaces when a new client connects.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_freq_new</span></code> option sets the maximum announce ingress
frequency for newly spawned interfaces. Defaults to <code class="docutils literal notranslate"><span class="pre">3.5</span></code>
announces per second.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_freq</span></code> option sets the maximum announce ingress
frequency for other interfaces. Defaults to <code class="docutils literal notranslate"><span class="pre">12</span></code> announces
per second.</div>
</div>
<blockquote>
<div><p><em>If an interface exceeds its burst frequency, incoming announces
for unknown destinations will be temporarily held in a queue, and
not processed until later.</em></p>
</div></blockquote>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_max_held_announces</span></code> option sets the maximum amount of
unique announces that will be held in the queue. Any additional
unique announces will be dropped. Defaults to <code class="docutils literal notranslate"><span class="pre">256</span></code> announces.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_hold</span></code> option sets how much time (in seconds) must
pass after the burst frequency drops below its threshold, for the
announce burst to be considered cleared. Defaults to <code class="docutils literal notranslate"><span class="pre">60</span></code>
seconds.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_burst_penalty</span></code> option sets how much time (in seconds) must
pass after the burst is considered cleared, before held announces can
start being released from the queue. Defaults to <code class="docutils literal notranslate"><span class="pre">5*60</span></code>
seconds.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ic_held_release_interval</span></code> option sets how much time (in seconds)
must pass between releasing each held announce from the queue. Defaults
to <code class="docutils literal notranslate"><span class="pre">30</span></code> seconds.</div>
</div>
</li>
</ul>
</div></blockquote>
</section>
</section>
</article>
@@ -995,7 +1097,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
<div class="toc-tree-container">
<div class="toc-tree">
<ul>
<li><a class="reference internal" href="#">Supported Interfaces</a><ul>
<li><a class="reference internal" href="#">Configuring Interfaces</a><ul>
<li><a class="reference internal" href="#auto-interface">Auto Interface</a></li>
<li><a class="reference internal" href="#i2p-interface">I2P Interface</a></li>
<li><a class="reference internal" href="#tcp-server-interface">TCP Server Interface</a></li>
@@ -1009,6 +1111,7 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
<li><a class="reference internal" href="#interface-modes">Interface Modes</a></li>
<li><a class="reference internal" href="#announce-rate-control">Announce Rate Control</a></li>
<li><a class="reference internal" href="#new-destination-rate-limiting">New Destination Rate Limiting</a></li>
</ul>
</li>
</ul>
@@ -1020,11 +1123,14 @@ that a large span of network types can seamlessly <em>co-exist</em> and intercon
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+20 -17
View File
@@ -3,15 +3,15 @@
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Supported Interfaces" href="interfaces.html" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Building Networks - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Building Networks - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -413,7 +413,7 @@ connected outliers are now an integral part of the network.</p>
<span>Previous</span>
</div>
<div class="title">Supported Interfaces</div>
<div class="title">Configuring Interfaces</div>
</div>
</a>
@@ -467,11 +467,14 @@ connected outliers are now an integral part of the network.</p>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
Binary file not shown.
+102 -23
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>API Reference - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>API Reference - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -253,7 +253,7 @@ other programs to use on demand.</p>
<dt class="sig sig-object py" id="RNS.Reticulum.MTU">
<span class="sig-name descname"><span class="pre">MTU</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">500</span></em><a class="headerlink" href="#RNS.Reticulum.MTU" title="Permalink to this definition">#</a></dt>
<dd><p>The MTU that Reticulum adheres to, and will expect other peers to
adhere to. By default, the MTU is 507 bytes. In custom RNS network
adhere to. By default, the MTU is 500 bytes. In custom RNS network
implementations, it is possible to change this value, but doing so will
completely break compatibility with all other RNS networks. An identical
MTU is a prerequisite for peers to communicate in the same network.</p>
@@ -275,7 +275,22 @@ links dont overwhelm the capacity of smaller networks on slower
mediums. If an announce remains queued for an extended amount of time,
it will eventually be dropped.</p>
<p>This value will be applied by default to all created interfaces,
but it can be configured individually on a per-interface basis.</p>
but it can be configured individually on a per-interface basis. In
general, the global default setting should not be changed, and any
alterations should be made on a per-interface basis instead.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Reticulum.MINIMUM_BITRATE">
<span class="sig-name descname"><span class="pre">MINIMUM_BITRATE</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">5</span></em><a class="headerlink" href="#RNS.Reticulum.MINIMUM_BITRATE" title="Permalink to this definition">#</a></dt>
<dd><p>Minimum bitrate required across a medium for Reticulum to be able
to successfully establish links. Currently 5 bits per second.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Reticulum.get_instance">
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_instance</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Reticulum.get_instance" title="Permalink to this definition">#</a></dt>
<dd><p>Return the currently running Reticulum instance</p>
</dd></dl>
<dl class="py method">
@@ -850,7 +865,7 @@ unless other app_data is specified in the <em>announce</em> method.</p>
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Packet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">create_receipt</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Packet" title="Permalink to this definition">#</a></dt>
<dd><p>The Packet class is used to create packet instances that can be sent
over a Reticulum network. Packets will automatically be encrypted if
they are adressed to a <code class="docutils literal notranslate"><span class="pre">RNS.Destination.SINGLE</span></code> destination,
they are addressed to a <code class="docutils literal notranslate"><span class="pre">RNS.Destination.SINGLE</span></code> destination,
<code class="docutils literal notranslate"><span class="pre">RNS.Destination.GROUP</span></code> destination or a <a class="reference internal" href="#api-link"><span class="std std-ref">RNS.Link</span></a>.</p>
<p>For <code class="docutils literal notranslate"><span class="pre">RNS.Destination.GROUP</span></code> destinations, Reticulum will use the
pre-shared key configured for the destination. All packets to group
@@ -1057,6 +1072,50 @@ thus preserved. This method can be used for authentication.</p>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.track_phy_stats">
<span class="sig-name descname"><span class="pre">track_phy_stats</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">track</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.track_phy_stats" title="Permalink to this definition">#</a></dt>
<dd><p>You can enable physical layer statistics on a per-link basis. If this is enabled,
and the link is running over an interface that supports reporting physical layer
statistics, you will be able to retrieve stats such as <em>RSSI</em>, <em>SNR</em> and physical
<em>Link Quality</em> for the link.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>track</strong> Whether or not to keep track of physical layer statistics. Value must be <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.get_rssi">
<span class="sig-name descname"><span class="pre">get_rssi</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_rssi" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The physical layer <em>Received Signal Strength Indication</em> if available, otherwise <code class="docutils literal notranslate"><span class="pre">None</span></code>. Physical layer statistics must be enabled on the link for this method to return a value.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.get_snr">
<span class="sig-name descname"><span class="pre">get_snr</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_snr" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The physical layer <em>Signal-to-Noise Ratio</em> if available, otherwise <code class="docutils literal notranslate"><span class="pre">None</span></code>. Physical layer statistics must be enabled on the link for this method to return a value.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.get_q">
<span class="sig-name descname"><span class="pre">get_q</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_q" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The physical layer <em>Link Quality</em> if available, otherwise <code class="docutils literal notranslate"><span class="pre">None</span></code>. Physical layer statistics must be enabled on the link for this method to return a value.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.get_establishment_rate">
<span class="sig-name descname"><span class="pre">get_establishment_rate</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_establishment_rate" title="Permalink to this definition">#</a></dt>
@@ -1072,7 +1131,7 @@ thus preserved. This method can be used for authentication.</p>
<span class="sig-name descname"><span class="pre">no_inbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_inbound_for" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The time in seconds since last inbound packet on the link.</p>
<dd class="field-odd"><p>The time in seconds since last inbound packet on the link. This includes keepalive packets.</p>
</dd>
</dl>
</dd></dl>
@@ -1082,7 +1141,17 @@ thus preserved. This method can be used for authentication.</p>
<span class="sig-name descname"><span class="pre">no_outbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_outbound_for" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The time in seconds since last outbound packet on the link.</p>
<dd class="field-odd"><p>The time in seconds since last outbound packet on the link. This includes keepalive packets.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.no_data_for">
<span class="sig-name descname"><span class="pre">no_data_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_data_for" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The time in seconds since payload data traversed the link. This excludes keepalive packets.</p>
</dd>
</dl>
</dd></dl>
@@ -1092,7 +1161,7 @@ thus preserved. This method can be used for authentication.</p>
<span class="sig-name descname"><span class="pre">inactive_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.inactive_for" title="Permalink to this definition">#</a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns<span class="colon">:</span></dt>
<dd class="field-odd"><p>The time in seconds since activity on the link.</p>
<dd class="field-odd"><p>The time in seconds since activity on the link. This includes keepalive packets.</p>
</dd>
</dl>
</dd></dl>
@@ -1536,7 +1605,7 @@ and <code class="docutils literal notranslate"><span class="pre">BufferedRWPair<
<code class="docutils literal notranslate"><span class="pre">RawChannelReader</span></code> and <code class="docutils literal notranslate"><span class="pre">RawChannelWriter</span></code>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Buffer.create_reader">
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_reader</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">BufferedReader</span></span></span><a class="headerlink" href="#RNS.Buffer.create_reader" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_reader</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">BufferedReader</span></span></span><a class="headerlink" href="#RNS.Buffer.create_reader" title="Permalink to this definition">#</a></dt>
<dd><p>Create a buffered reader that reads binary data sent
over a <code class="docutils literal notranslate"><span class="pre">Channel</span></code>, with an optional callback when
new data is available.</p>
@@ -1581,7 +1650,7 @@ of this object, see the Python documentation for
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Buffer.create_bidirectional_buffer">
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_bidirectional_buffer</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">receive_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">send_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">BufferedRWPair</span></span></span><a class="headerlink" href="#RNS.Buffer.create_bidirectional_buffer" title="Permalink to this definition">#</a></dt>
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">create_bidirectional_buffer</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">receive_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">send_stream_id</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">channel</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#RNS.Channel.Channel" title="RNS.Channel.Channel"><span class="pre">Channel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">ready_callback</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">Callable</span><span class="p"><span class="pre">[</span></span><span class="p"><span class="pre">[</span></span><span class="pre">int</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">None</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">BufferedRWPair</span></span></span><a class="headerlink" href="#RNS.Buffer.create_bidirectional_buffer" title="Permalink to this definition">#</a></dt>
<dd><p>Create a buffered reader/writer pair that reads and
writes binary data over a <code class="docutils literal notranslate"><span class="pre">Channel</span></code>, with an
optional callback when new data is available.</p>
@@ -1851,6 +1920,8 @@ will announce it.</p>
<li><a class="reference internal" href="#RNS.Reticulum"><code class="docutils literal notranslate"><span class="pre">Reticulum</span></code></a><ul>
<li><a class="reference internal" href="#RNS.Reticulum.MTU"><code class="docutils literal notranslate"><span class="pre">MTU</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.ANNOUNCE_CAP"><code class="docutils literal notranslate"><span class="pre">ANNOUNCE_CAP</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.MINIMUM_BITRATE"><code class="docutils literal notranslate"><span class="pre">MINIMUM_BITRATE</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.get_instance"><code class="docutils literal notranslate"><span class="pre">get_instance()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.should_use_implicit_proof"><code class="docutils literal notranslate"><span class="pre">should_use_implicit_proof()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Reticulum.transport_enabled"><code class="docutils literal notranslate"><span class="pre">transport_enabled()</span></code></a></li>
</ul>
@@ -1924,9 +1995,14 @@ will announce it.</p>
<li><a class="reference internal" href="#RNS.Link.STALE_TIME"><code class="docutils literal notranslate"><span class="pre">STALE_TIME</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.identify"><code class="docutils literal notranslate"><span class="pre">identify()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.request"><code class="docutils literal notranslate"><span class="pre">request()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.track_phy_stats"><code class="docutils literal notranslate"><span class="pre">track_phy_stats()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.get_rssi"><code class="docutils literal notranslate"><span class="pre">get_rssi()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.get_snr"><code class="docutils literal notranslate"><span class="pre">get_snr()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.get_q"><code class="docutils literal notranslate"><span class="pre">get_q()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.get_establishment_rate"><code class="docutils literal notranslate"><span class="pre">get_establishment_rate()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.no_inbound_for"><code class="docutils literal notranslate"><span class="pre">no_inbound_for()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.no_outbound_for"><code class="docutils literal notranslate"><span class="pre">no_outbound_for()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.no_data_for"><code class="docutils literal notranslate"><span class="pre">no_data_for()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.inactive_for"><code class="docutils literal notranslate"><span class="pre">inactive_for()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.get_remote_identity"><code class="docutils literal notranslate"><span class="pre">get_remote_identity()</span></code></a></li>
<li><a class="reference internal" href="#RNS.Link.teardown"><code class="docutils literal notranslate"><span class="pre">teardown()</span></code></a></li>
@@ -2013,11 +2089,14 @@ will announce it.</p>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+16 -13
View File
@@ -4,11 +4,11 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.5.9 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.7.4 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -138,7 +138,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -158,13 +158,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -178,7 +178,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -262,12 +262,15 @@
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
<script src="_static/searchtools.js"></script>
<script src="_static/language_data.js"></script>
File diff suppressed because one or more lines are too long
+18 -15
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Support Reticulum - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Support Reticulum - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Support Reticulum</a></li>
@@ -330,11 +330,14 @@ report issues, suggest functionality and contribute code to Reticulum.</p>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+20 -17
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Understanding Reticulum - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Understanding Reticulum - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -286,7 +286,7 @@ it can be easily modified and replicated by anyone interested in doing so.</p>
</li>
<li><dl class="simple">
<dt><strong>Very low bandwidth requirements</strong></dt><dd><p>Reticulum should be able to function reliably over links with a transmission capacity as low
as <em>500 bits per second</em>.</p>
as <em>5 bits per second</em>.</p>
</dd>
</dl>
</li>
@@ -831,7 +831,7 @@ note that Reticulum is designed to be usable on more or less any computing devic
or less any medium that allows you to send and receive data, which satisfies some very low
minimum requirements.</p>
<p>The communication channel must support at least half-duplex operation, and provide an average
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes. The
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
runtime environment.</p>
<p>That being said, this reference setup has been outlined to provide a common platform for anyone
@@ -1196,11 +1196,14 @@ those risks are acceptable to you.</p>
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+133 -27
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -307,6 +307,17 @@ configuration file is created. The default configuration looks like this:</p>
<span class="n">instance_control_port</span> <span class="o">=</span> <span class="mi">37429</span>
<span class="c1"># On systems where running instances may not have access</span>
<span class="c1"># to the same shared Reticulum configuration directory,</span>
<span class="c1"># it is still possible to allow full interactivity for</span>
<span class="c1"># running instances, by manually specifying a shared RPC</span>
<span class="c1"># key. In almost all cases, this option is not needed, but</span>
<span class="c1"># it can be useful on operating systems such as Android.</span>
<span class="c1"># The key must be specified as bytes in hexadecimal.</span>
<span class="c1"># rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790</span>
<span class="c1"># You can configure Reticulum to panic and forcibly close</span>
<span class="c1"># if an unrecoverable interface error occurs, such as the</span>
<span class="c1"># hardware device for an interface disappearing. This is</span>
@@ -316,6 +327,17 @@ configuration file is created. The default configuration looks like this:</p>
<span class="n">panic_on_interface_error</span> <span class="o">=</span> <span class="n">No</span>
<span class="c1"># When Transport is enabled, it is possible to allow the</span>
<span class="c1"># Transport Instance to respond to probe requests from</span>
<span class="c1"># the rnprobe utility. This can be a useful tool to test</span>
<span class="c1"># connectivity. When this option is enabled, the probe</span>
<span class="c1"># destination will be generated from the Identity of the</span>
<span class="c1"># Transport Instance, and printed to the log at startup.</span>
<span class="c1"># Optional, and disabled by default.</span>
<span class="n">respond_to_probes</span> <span class="o">=</span> <span class="n">No</span>
<span class="p">[</span><span class="n">logging</span><span class="p">]</span>
<span class="c1"># Valid log levels are 0 through 7:</span>
<span class="c1"># 0: Log only critical information</span>
@@ -388,6 +410,15 @@ the same system.</p>
[2023-08-18 17:59:56] [Notice] Started rnsd version 0.5.8
</pre></div>
</div>
<p>Run <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> in service mode, ensuring all logging output is sent directly to file:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnsd -s
</pre></div>
</div>
<p>Generate a verbose and detailed configuration example, with explanations of all the
various configuration options, and interface configuration examples:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnsd --exampleconfig
</pre></div>
</div>
<p><strong>All Command-Line Options</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnsd.py [-h] [--config CONFIG] [-v] [-q] [-s] [--exampleconfig] [--version]
@@ -461,19 +492,23 @@ Reticulum Transport Instance &lt;5245a8efe1788c6a1cd36144a270e13b&gt; running
</pre></div>
</div>
<p><strong>All Command-Line Options</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-j] [-v] [filter]
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
[-r] [-j] [-v] [filter]
Reticulum Network Stack Status
positional arguments:
filter only display interfaces with names including filter
filter only display interfaces with names including filter
options:
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
--version show program&#39;s version number and exit
-a, --all show all interfaces
-j, --json output in JSON format
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
--version show program&#39;s version number and exit
-a, --all show all interfaces
-A, --announce-stats show announce stats
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
-r, --reverse reverse sorting
-j, --json output in JSON format
-v, --verbose
</pre></div>
</div>
@@ -575,7 +610,7 @@ Path found, destination &lt;c89b4da064bf66d280f0e4d8abfd9806&gt; is 4 hops away
</div>
<p><strong>All Command-Line Options</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
[-w seconds] [-v] [destination]
[-x] [-w seconds] [-v] [destination]
Reticulum Path Discovery Utility
@@ -590,6 +625,7 @@ options:
-r, --rates show announce rate info
-d, --drop remove the path to a destination
-D, --drop-announces drop all queued announces
-x, --drop-via drop all paths via specified transport instance
-w seconds timeout before giving up
-v, --verbose
</pre></div>
@@ -600,18 +636,41 @@ options:
<p>The <code class="docutils literal notranslate"><span class="pre">rnprobe</span></code> utility lets you probe a destination for connectivity, similar
to the <code class="docutils literal notranslate"><span class="pre">ping</span></code> program. Please note that probes will only be answered if the
specified destination is configured to send proofs for received packets. Many
destinations will not have this option enabled, and will not be probable.</p>
destinations will not have this option enabled, so most destinations will not
be probable.</p>
<p>You can enable a probe-reply destination on Reticulum Transport Instances by
setting the <code class="docutils literal notranslate"><span class="pre">respond_to_probes</span></code> configuration directive. Reticulum will then
print the probe destination to the log on Transport Instance startup.</p>
<p><strong>Usage Examples</strong></p>
<p>Probe a destination:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe example_utilities.echo.request 2d03725b327348980d570f739a3a5708
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708
Sent 16 byte probe to &lt;2d03725b327348980d570f739a3a5708&gt;
Valid reply received from &lt;2d03725b327348980d570f739a3a5708&gt;
Round-trip time is 38.469 milliseconds over 2 hops
</pre></div>
</div>
<p>Send a larger probe:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708 -s 256
Sent 16 byte probe to &lt;2d03725b327348980d570f739a3a5708&gt;
Valid reply received from &lt;2d03725b327348980d570f739a3a5708&gt;
Round-trip time is 38.781 milliseconds over 2 hops
</pre></div>
</div>
<p>If the interface that receives the probe replies supports reporting radio
parameters such as <strong>RSSI</strong> and <strong>SNR</strong>, the <code class="docutils literal notranslate"><span class="pre">rnprobe</span></code> utility will print
these as part of the result as well.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rnprobe rnstransport.probe e7536ee90bd4a440e130490b87a25124
Sent 16 byte probe to &lt;e7536ee90bd4a440e130490b87a25124&gt;
Valid reply received from &lt;e7536ee90bd4a440e130490b87a25124&gt;
Round-trip time is 1.809 seconds over 1 hop [RSSI -73 dBm] [SNR 12.0 dB]
</pre></div>
</div>
<p><strong>All Command-Line Options</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [-s SIZE]
[full_name] [destination_hash]
Reticulum Probe Utility
@@ -622,6 +681,7 @@ positional arguments:
optional arguments:
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
-s SIZE, --size SIZE size of probe packet payload in bytes
--version show program&#39;s version number and exit
-v, --verbose
</pre></div>
@@ -826,6 +886,8 @@ assignment varies from one boot to another.</p>
<span id="using-systemd"></span><h3>Reticulum as a System Service<a class="headerlink" href="#reticulum-as-a-system-service" title="Permalink to this heading">#</a></h3>
<p>Instead of starting Reticulum manually, you can install <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> as a system
service and have it start automatically at boot.</p>
<section id="systemwide-service">
<h4>Systemwide Service<a class="headerlink" href="#systemwide-service" title="Permalink to this heading">#</a></h4>
<p>If you installed Reticulum with <code class="docutils literal notranslate"><span class="pre">pip</span></code>, the <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> program will most likely
be located in a user-local installation path only, which means <code class="docutils literal notranslate"><span class="pre">systemd</span></code> will not
be able to execute it. In this case, you can simply symlink the <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> program
@@ -866,6 +928,43 @@ WantedBy=multi-user.target
</pre></div>
</div>
</section>
<section id="userspace-service">
<h4>Userspace Service<a class="headerlink" href="#userspace-service" title="Permalink to this heading">#</a></h4>
<p>Alternatively you can use a user systemd service instead of a system wide one. This way the whole setup can be done as a regular user.
Create a user systemd service files <code class="docutils literal notranslate"><span class="pre">~/.config/systemd/user/rnsd.service</span></code> with the following content:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[Unit]
Description=Reticulum Network Stack Daemon
After=default.target
[Service]
# If you run Reticulum on WiFi devices,
# or other devices that need some extra
# time to initialise, you might want to
# add a short delay before Reticulum is
# started by systemd:
# ExecStartPre=/bin/sleep 10
Type=simple
Restart=always
RestartSec=3
ExecStart=RNS_BIN_DIR/rnsd --service
[Install]
WantedBy=default.target
</pre></div>
</div>
<p>Replace <code class="docutils literal notranslate"><span class="pre">RNS_BIN_DIR</span></code> with the path to your Reticulum binary directory (eg. /home/USERNAMEHERE/rns/bin).</p>
<p>Start user service:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>systemctl --user daemon-reload
systemctl --user start rnsd.service
</pre></div>
</div>
<p>If you want to automatically start <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> without having to log in as the USERNAMEHERE, do:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>sudo loginctl enable-linger USERNAMEHERE
systemctl --user enable rnsd.service
</pre></div>
</div>
</section>
</section>
</section>
</section>
@@ -940,7 +1039,11 @@ WantedBy=multi-user.target
</li>
<li><a class="reference internal" href="#improving-system-configuration">Improving System Configuration</a><ul>
<li><a class="reference internal" href="#fixed-serial-port-names">Fixed Serial Port Names</a></li>
<li><a class="reference internal" href="#reticulum-as-a-system-service">Reticulum as a System Service</a></li>
<li><a class="reference internal" href="#reticulum-as-a-system-service">Reticulum as a System Service</a><ul>
<li><a class="reference internal" href="#systemwide-service">Systemwide Service</a></li>
<li><a class="reference internal" href="#userspace-service">Userspace Service</a></li>
</ul>
</li>
</ul>
</li>
</ul>
@@ -954,11 +1057,14 @@ WantedBy=multi-user.target
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+19 -16
View File
@@ -5,13 +5,13 @@
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
<meta name="generator" content="sphinx-7.1.2, furo 2022.09.29.dev1"/>
<title>What is Reticulum? - Reticulum Network Stack 0.5.9 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>What is Reticulum? - Reticulum Network Stack 0.7.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
@@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.5.9 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -161,13 +161,13 @@
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.5.9 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -181,7 +181,7 @@
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
@@ -293,7 +293,7 @@ considered complete and stable at the moment, but could change if absolutely war
<section id="where-can-reticulum-be-used">
<h2>Where can Reticulum be Used?<a class="headerlink" href="#where-can-reticulum-be-used" title="Permalink to this heading">#</a></h2>
<p>Over practically any medium that can support at least a half-duplex channel
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
ad-hoc WiFi, free-space optical links and similar systems are all examples
of the types of interfaces Reticulum was designed for.</p>
@@ -434,11 +434,14 @@ want to help out with this, or can help sponsor an audit, please do get in touch
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js?v=f24f4563"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=4825356b"></script>
<script src="_static/scripts/furo.js?v=2c7c1115"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>
+11 -11
View File
@@ -180,29 +180,29 @@ and :ref:`Interfaces<interfaces-main>` chapters of this manual.
Connecting Reticulum Instances Over the Internet
================================================
Reticulum currently offers two interfaces suitable for connecting instances over the Internet: :ref:`TCP<interfaces-tcps>`
and :ref:`I2P<interfaces-i2p>`. Each interface offers a different set of features, and Reticulum
users should carefully choose the interface which best suites their needs.
and :ref:`I2P<interfaces-i2p>`. Each interface offers a different set of features, and Reticulum
users should carefully choose the interface which best suites their needs.
The ``TCPServerInterface`` allows users to host an instance accessible over TCP/IP. This
method is generally faster, lower latency, and more energy efficient than using ``I2PInterface``,
however it also leaks more data about the server host.
TCP connections reveal the IP address of both your instance and the server to anyone who can
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
inspect the connection. Someone could use this information to determine your location or identity. Adversaries
inspecting your packets may be able to record packet metadata like time of transmission and packet size.
Even though Reticulum encrypts traffic, TCP does not, so an adversary may be able to use
packet inspection to learn that a system is running Reticulum, and what other IP addresses connect to it.
Hosting a publicly reachable instance over TCP also requires a publicly reachable IP address,
which most Internet connections don't offer anymore.
The ``I2PInterface`` routes messages through the `Invisible Internet Protocol
The ``I2PInterface`` routes messages through the `Invisible Internet Protocol
(I2P) <https://geti2p.net/en/>`_. To use this interface, users must also run an I2P daemon in
parallel to ``rnsd``. For always-on I2P nodes it is recommended to use `i2pd <https://i2pd.website/>`_.
parallel to ``rnsd``. For always-on I2P nodes it is recommended to use `i2pd <https://i2pd.website/>`_.
By default, I2P will encrypt and mix all traffic sent over the Internet, and
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
By default, I2P will encrypt and mix all traffic sent over the Internet, and
hide both the sender and receiver Reticulum instance IP addresses. Running an I2P node
will also relay other I2P user's encrypted packets, which will use extra
bandwidth and compute power, but also makes timing attacks and other forms of
bandwidth and compute power, but also makes timing attacks and other forms of
deep-packet-inspection much more difficult.
I2P also allows users to host globally available Reticulum instances from non-public IP's and behind firewalls and NAT.
@@ -415,7 +415,7 @@ locally on your device using the following command:
It is also possible to include Reticulum in apps compiled and distributed as
Android APKs. A detailed tutorial and example source code will be included
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and startig point.
here at a later point. Until then you can use the `Sideband source code <https://github.com/markqvist/sideband>`_ as an example and starting point.
ARM64
@@ -455,7 +455,7 @@ for including and using Reticulum in your own scripts and programs.
.. code::
# Install pipx
# Install pipx
sudo apt install pipx
# Make installed programs available on the command line
@@ -489,7 +489,7 @@ for including and using Reticulum in your own scripts and programs.
.. code::
# Install pipx
# Install pipx
sudo apt install pipx
# Make installed programs available on the command line
+94 -6
View File
@@ -1,9 +1,9 @@
.. _interfaces-main:
********************
Supported Interfaces
********************
**********************
Configuring Interfaces
**********************
Reticulum supports using many kinds of devices as networking interfaces, and
allows you to mix and match them in any way you choose. The number of distinct
@@ -33,9 +33,20 @@ system, which should be enabled by default in almost all OSes.
.. code::
# This example demonstrates a TCP server interface.
# It will listen for incoming connections on the
# specified IP address and port number.
# This example demonstrates a bare-minimum setup
# of an Auto Interface. It will allow communica-
# tion with all other reachable devices on all
# usable physical ethernet-based devices that
# are available on the system.
[[Default Interface]]
type = AutoInterface
interface_enabled = True
# This example demonstrates an more specifically
# configured Auto Interface, that only uses spe-
# cific physical interfaces, and has a number of
# other configuration options set.
[[Default Interface]]
type = AutoInterface
@@ -47,6 +58,12 @@ system, which should be enabled by default in almost all OSes.
group_id = reticulum
# You can also choose the multicast address type:
# temporary (default, Temporary Multicast Address)
# or permanent (Permanent Multicast Address)
multicast_address_type = permanent
# You can also select specifically which
# kernel networking devices to use.
@@ -761,3 +778,74 @@ conserve bandwidth, while very fast networks can support applications that
need very frequent announces. Reticulum implements these mechanisms to ensure
that a large span of network types can seamlessly *co-exist* and interconnect.
.. _interfaces-ingress-control:
New Destination Rate Limiting
=============================
On public interfaces, where anyone may connect and announce new destinations,
it can be useful to control the rate at which announces for *new* destinations are
processed.
If a large influx of announces for newly created or previously unknown destinations
occur within a short amount of time, Reticulum will place these announces on hold,
so that announce traffic for known and previously established destinations can
continue to be processed without interruptions.
After the burst subsides, and an additional waiting period has passed, the held
announces will be released at a slow rate, until the hold queue is cleared. This
also means, that should a node decide to connect to a public interface, announce
a large amount of bogus destinations, and then disconnect, these destination will
never make it into path tables and waste network bandwidth on retransmitted
announces.
**It's important to note** that the ingress control works at the level of *individual
sub-interfaces*. As an example, this means that one client on a :ref:`TCP Server Interface<interfaces-tcps>`
cannot disrupt processing of incoming announces for other connected clients on the same
:ref:`TCP Server Interface<interfaces-tcps>`. All other clients on the same interface will still have new announces
processed without interruption.
By default, Reticulum will handle this automatically, and ingress announce
control will be enabled on interface where it is sensible to do so. It should
generally not be neccessary to modify the ingress control configuration,
but all the parameters are exposed for configuration if needed.
* | The ``ingress_control`` option tells Reticulum whether or not
to enable announce ingress control on the interface. Defaults to
``True``.
* | The ``ic_new_time`` option configures how long (in seconds) an
interface is considered newly spawned. Defaults to ``2*60*60`` seconds. This
option is useful on publicly accessible interfaces that spawn new
sub-interfaces when a new client connects.
* | The ``ic_burst_freq_new`` option sets the maximum announce ingress
frequency for newly spawned interfaces. Defaults to ``3.5``
announces per second.
* | The ``ic_burst_freq`` option sets the maximum announce ingress
frequency for other interfaces. Defaults to ``12`` announces
per second.
*If an interface exceeds its burst frequency, incoming announces
for unknown destinations will be temporarily held in a queue, and
not processed until later.*
* | The ``ic_max_held_announces`` option sets the maximum amount of
unique announces that will be held in the queue. Any additional
unique announces will be dropped. Defaults to ``256`` announces.
* | The ``ic_burst_hold`` option sets how much time (in seconds) must
pass after the burst frequency drops below its threshold, for the
announce burst to be considered cleared. Defaults to ``60``
seconds.
* | The ``ic_burst_penalty`` option sets how much time (in seconds) must
pass after the burst is considered cleared, before held announces can
start being released from the queue. Defaults to ``5*60``
seconds.
* | The ``ic_held_release_interval`` option sets how much time (in seconds)
must pass between releasing each held announce from the queue. Defaults
to ``30`` seconds.
+2 -2
View File
@@ -75,7 +75,7 @@ guide the design of Reticulum:
it can be easily modified and replicated by anyone interested in doing so.
* **Very low bandwidth requirements**
Reticulum should be able to function reliably over links with a transmission capacity as low
as *500 bits per second*.
as *5 bits per second*.
* **Encryption by default**
Reticulum must use strong encryption by default for all communication.
* **Initiator Anonymity**
@@ -595,7 +595,7 @@ or less any medium that allows you to send and receive data, which satisfies som
minimum requirements.
The communication channel must support at least half-duplex operation, and provide an average
throughput of around 500 bits per second, and supports a physical layer MTU of 500 bytes. The
throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes. The
Reticulum stack should be able to run on more or less any hardware that can provide a Python 3.x
runtime environment.
+128 -11
View File
@@ -99,6 +99,17 @@ configuration file is created. The default configuration looks like this:
instance_control_port = 37429
# On systems where running instances may not have access
# to the same shared Reticulum configuration directory,
# it is still possible to allow full interactivity for
# running instances, by manually specifying a shared RPC
# key. In almost all cases, this option is not needed, but
# it can be useful on operating systems such as Android.
# The key must be specified as bytes in hexadecimal.
# rpc_key = e5c032d3ec4e64a6aca9927ba8ab73336780f6d71790
# You can configure Reticulum to panic and forcibly close
# if an unrecoverable interface error occurs, such as the
# hardware device for an interface disappearing. This is
@@ -108,6 +119,17 @@ configuration file is created. The default configuration looks like this:
panic_on_interface_error = No
# When Transport is enabled, it is possible to allow the
# Transport Instance to respond to probe requests from
# the rnprobe utility. This can be a useful tool to test
# connectivity. When this option is enabled, the probe
# destination will be generated from the Identity of the
# Transport Instance, and printed to the log at startup.
# Optional, and disabled by default.
respond_to_probes = No
[logging]
# Valid log levels are 0 through 7:
# 0: Log only critical information
@@ -192,6 +214,19 @@ Run ``rnsd``:
[2023-08-18 17:59:56] [Notice] Started rnsd version 0.5.8
Run ``rnsd`` in service mode, ensuring all logging output is sent directly to file:
.. code:: text
$ rnsd -s
Generate a verbose and detailed configuration example, with explanations of all the
various configuration options, and interface configuration examples:
.. code:: text
$ rnsd --exampleconfig
**All Command-Line Options**
.. code:: text
@@ -277,19 +312,23 @@ Filter output to only show some interfaces:
.. code:: text
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-j] [-v] [filter]
usage: rnstatus.py [-h] [--config CONFIG] [--version] [-a] [-A] [-s SORT]
[-r] [-j] [-v] [filter]
Reticulum Network Stack Status
positional arguments:
filter only display interfaces with names including filter
filter only display interfaces with names including filter
options:
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
--version show program's version number and exit
-a, --all show all interfaces
-j, --json output in JSON format
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
--version show program's version number and exit
-a, --all show all interfaces
-A, --announce-stats show announce stats
-s SORT, --sort SORT sort interfaces by [rate, traffic, rx, tx, announces, arx, atx, held]
-r, --reverse reverse sorting
-j, --json output in JSON format
-v, --verbose
@@ -414,7 +453,7 @@ Resolve path to a destination:
.. code:: text
usage: rnpath.py [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D]
[-w seconds] [-v] [destination]
[-x] [-w seconds] [-v] [destination]
Reticulum Path Discovery Utility
@@ -429,6 +468,7 @@ Resolve path to a destination:
-r, --rates show announce rate info
-d, --drop remove the path to a destination
-D, --drop-announces drop all queued announces
-x, --drop-via drop all paths via specified transport instance
-w seconds timeout before giving up
-v, --verbose
@@ -439,7 +479,12 @@ The rnprobe Utility
The ``rnprobe`` utility lets you probe a destination for connectivity, similar
to the ``ping`` program. Please note that probes will only be answered if the
specified destination is configured to send proofs for received packets. Many
destinations will not have this option enabled, and will not be probable.
destinations will not have this option enabled, so most destinations will not
be probable.
You can enable a probe-reply destination on Reticulum Transport Instances by
setting the ``respond_to_probes`` configuration directive. Reticulum will then
print the probe destination to the log on Transport Instance startup.
**Usage Examples**
@@ -447,17 +492,40 @@ Probe a destination:
.. code:: text
$ rnprobe example_utilities.echo.request 2d03725b327348980d570f739a3a5708
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
Valid reply received from <2d03725b327348980d570f739a3a5708>
Round-trip time is 38.469 milliseconds over 2 hops
Send a larger probe:
.. code:: text
$ rnprobe rnstransport.probe 2d03725b327348980d570f739a3a5708 -s 256
Sent 16 byte probe to <2d03725b327348980d570f739a3a5708>
Valid reply received from <2d03725b327348980d570f739a3a5708>
Round-trip time is 38.781 milliseconds over 2 hops
If the interface that receives the probe replies supports reporting radio
parameters such as **RSSI** and **SNR**, the ``rnprobe`` utility will print
these as part of the result as well.
.. code:: text
$ rnprobe rnstransport.probe e7536ee90bd4a440e130490b87a25124
Sent 16 byte probe to <e7536ee90bd4a440e130490b87a25124>
Valid reply received from <e7536ee90bd4a440e130490b87a25124>
Round-trip time is 1.809 seconds over 1 hop [RSSI -73 dBm] [SNR 12.0 dB]
**All Command-Line Options**
.. code:: text
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [-s SIZE]
[full_name] [destination_hash]
Reticulum Probe Utility
@@ -468,6 +536,7 @@ Probe a destination:
optional arguments:
-h, --help show this help message and exit
--config CONFIG path to alternative Reticulum config directory
-s SIZE, --size SIZE size of probe packet payload in bytes
--version show program's version number and exit
-v, --verbose
@@ -711,6 +780,9 @@ Reticulum as a System Service
Instead of starting Reticulum manually, you can install ``rnsd`` as a system
service and have it start automatically at boot.
Systemwide Service
^^^^^^^^^^^^^^^^^^
If you installed Reticulum with ``pip``, the ``rnsd`` program will most likely
be located in a user-local installation path only, which means ``systemd`` will not
be able to execute it. In this case, you can simply symlink the ``rnsd`` program
@@ -758,3 +830,48 @@ If you want to automatically start ``rnsd`` at boot, run:
.. code:: text
sudo systemctl enable rnsd
Userspace Service
^^^^^^^^^^^^^^^^^
Alternatively you can use a user systemd service instead of a system wide one. This way the whole setup can be done as a regular user.
Create a user systemd service files ``~/.config/systemd/user/rnsd.service`` with the following content:
.. code:: text
[Unit]
Description=Reticulum Network Stack Daemon
After=default.target
[Service]
# If you run Reticulum on WiFi devices,
# or other devices that need some extra
# time to initialise, you might want to
# add a short delay before Reticulum is
# started by systemd:
# ExecStartPre=/bin/sleep 10
Type=simple
Restart=always
RestartSec=3
ExecStart=RNS_BIN_DIR/rnsd --service
[Install]
WantedBy=default.target
Replace ``RNS_BIN_DIR`` with the path to your Reticulum binary directory (eg. /home/USERNAMEHERE/rns/bin).
Start user service:
.. code:: text
systemctl --user daemon-reload
systemctl --user start rnsd.service
If you want to automatically start ``rnsd`` without having to log in as the USERNAMEHERE, do:
.. code:: text
sudo loginctl enable-linger USERNAMEHERE
systemctl --user enable rnsd.service
+1 -1
View File
@@ -91,7 +91,7 @@ What does Reticulum Offer?
Where can Reticulum be Used?
============================
Over practically any medium that can support at least a half-duplex channel
with 500 bits per second throughput, and an MTU of 500 bytes. Data radios,
with greater throughput than 5 bits per second, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
ad-hoc WiFi, free-space optical links and similar systems are all examples
of the types of interfaces Reticulum was designed for.
+1
View File
@@ -49,6 +49,7 @@ setuptools.setup(
'rnid=RNS.Utilities.rnid:main',
'rncp=RNS.Utilities.rncp:main',
'rnx=RNS.Utilities.rnx:main',
'rnir=RNS.Utilities.rnir:main',
'rnodeconf=RNS.Utilities.rnodeconf:main',
]
},
+44 -4
View File
@@ -99,6 +99,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -120,6 +123,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -140,6 +146,7 @@ class TestLink(unittest.TestCase):
pstart = time.time()
print("Sending "+str(num_packets)+" link packets of "+str(packet_size)+" bytes...")
for i in range(0, num_packets):
time.sleep(0.003)
b += packet_size
data = os.urandom(packet_size)
start = time.time()
@@ -188,6 +195,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -203,6 +213,7 @@ class TestLink(unittest.TestCase):
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
@@ -224,6 +235,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -239,6 +253,7 @@ class TestLink(unittest.TestCase):
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
@@ -260,6 +275,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -274,6 +292,7 @@ class TestLink(unittest.TestCase):
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
@@ -300,6 +319,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -314,6 +336,7 @@ class TestLink(unittest.TestCase):
resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time()
# This is a hack, don't do it. Use the callbacks instead.
while resource.status < RNS.Resource.COMPLETE:
time.sleep(0.01)
@@ -325,6 +348,10 @@ class TestLink(unittest.TestCase):
time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.CLOSED)
large_resource_status = None
def lr_callback(self, resource):
TestLink.large_resource_status = resource.status
@skipIf(os.getenv('SKIP_NORMAL_TESTS') != None, "Skipping")
def test_9_large_resource(self):
if RNS.Cryptography.backend() == "internal":
@@ -339,6 +366,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -347,17 +377,18 @@ class TestLink(unittest.TestCase):
self.assertEqual(l1.status, RNS.Link.ACTIVE)
resource_timeout = 120
resource_size = 35*1000*1000
resource_size = 50*1000*1000
data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout)
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
start = time.time()
while resource.status < RNS.Resource.COMPLETE:
TestLink.large_resource_status = resource.status
while TestLink.large_resource_status < RNS.Resource.COMPLETE:
time.sleep(0.01)
t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE)
self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps")
l1.teardown()
@@ -375,6 +406,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -424,6 +458,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
@@ -477,6 +514,9 @@ class TestLink(unittest.TestCase):
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
self.assertEqual(id1.hash, bytes.fromhex(fixed_keys[0][1]))
RNS.Transport.request_path(bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
time.sleep(0.2)
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
+1 -1
View File
@@ -9,4 +9,4 @@
loglevel = 1
[interfaces]
# No interfaces, only local traffic
# No interfaces, only local traffic