mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-06-22 20:12:37 -07:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6df6293b2 | |||
| d99d31097b | |||
| 54488cfeb5 | |||
| d7e38d646e | |||
| b9057bee5f | |||
| 9bd64834ec | |||
| 9e20ba2dac | |||
| 49ed335e19 | |||
| 85c71b0b7b | |||
| 33fac728f8 | |||
| 49616a36cf | |||
| 1e77f85cd4 | |||
| 9e316ab989 | |||
| 94749e0dde | |||
| a6dbc53209 | |||
| 3af5a8f3ed | |||
| fb5172ff10 | |||
| 24d6de8490 | |||
| d3ab0878e0 | |||
| 7848b7e396 | |||
| fc80dd2614 | |||
| e00a758b2a | |||
| d44ec745df | |||
| 7573ac1970 | |||
| 88390f0cbc | |||
| 3b8490ae9c | |||
| 417ac9f8da | |||
| fe5e74bc2b | |||
| 30f71857ae | |||
| c24233845e | |||
| c0fbde5ad1 | |||
| 5da66402dd | |||
| 3bf5694238 | |||
| 9e6a5d5d91 | |||
| cf3e47f469 | |||
| f8db5a545d | |||
| a79f6e7efa | |||
| ac4606bcf7 | |||
| d1cb07356c | |||
| e811d54d0f | |||
| 49c8ada478 | |||
| 6ea7d78b31 | |||
| 0ace84367b | |||
| e63e6821e0 | |||
| 109132e09d | |||
| efd24ec134 | |||
| eefa37f808 | |||
| e4871f7667 | |||
| 44ba5624bc | |||
| e9c5e3c189 | |||
| f3ff71d9b8 | |||
| 81b92ffdc1 | |||
| 02bb9068cc | |||
| ecc9e84bc2 | |||
| 2b43436f56 | |||
| b2d61843d0 | |||
| ff74b5a0af | |||
| d66c31b4e9 | |||
| e825b0b8ff | |||
| b35f86643a | |||
| 3871d8615e | |||
| f2c0dac217 | |||
| 8636259886 | |||
| 4b38a776a3 | |||
| 7a331a8b60 | |||
| af1a05ff6a | |||
| 1b50f5267a |
@@ -5,9 +5,9 @@ Reticulum Network Stack β
|
||||
|
||||
Reticulum is the cryptography-based networking stack for wide-area networks built on readily available hardware. It can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build wide-area networks with off-the-shelf tools, and offers end-to-end encryption and connectivity, initiator anonymity, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable delivery acknowledgements and more.
|
||||
|
||||
The vision of Reticulum is to allow anyone to be their own network operator, and to make it cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks. Reticulum **is not** *one network*, it **is a tool** for building *thousands of networks*. Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
The vision of Reticulum is to allow anyone to be their own network operator, and to make it cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks. Reticulum **is not** *one* network. It is **a tool** for building *thousands of networks*. Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
|
||||
Reticulum is a complete networking stack, and does not need IP or higher layers, although it is easy to use IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks.
|
||||
Reticulum is a complete networking stack, and does not rely on IP or higher layers, but it is possible to use IP as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks.
|
||||
|
||||
Having no dependencies on traditional networking stacks free up overhead that has been utilised to implement a networking stack built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
|
||||
|
||||
@@ -75,6 +75,18 @@ When first started, Reticulum will create a default configuration file, providin
|
||||
|
||||
You can use the examples in the config file to expand communication over many mediums such as packet radio or LoRa (with [RNode](https://unsigned.io/projects/rnode/)), serial ports, or over fast IP links and the Internet using the UDP and TCP interfaces. For more detailed examples, take a look at the [Supported Interfaces](https://markqvist.github.io/Reticulum/manual/interfaces.html) section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
||||
|
||||
## Included Utilities
|
||||
Reticulum includes a range of useful utilities for managing your networks, viewing status and information, and other tasks. You can read more about these programs in the [Included Utility Programs](https://markqvist.github.io/Reticulum/manual/using.html#included-utility-programs) section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
||||
|
||||
- The system daemon `rnsd` for running Reticulum as an always-available service
|
||||
- An interface status utility called `rnstatus`, that displays information about interfaces
|
||||
- The path lookup and and management tool `rnpath` letting you view and modify path tables
|
||||
- A diagnostics tool called `rnprobe` for checking connectivity to destinations
|
||||
- A simple file transfer program called `rncp` making easy to copy files to remote systems
|
||||
- The remote command execution program `rnx` that let's you run commands and programs and retrieve output from remote systems
|
||||
|
||||
All tools, including `rnx` and `rncp`, work reliably and well even over very low-bandwidth links like LoRa or Packet Radio.
|
||||
|
||||
## Current Status
|
||||
Reticulum should currently be considered beta software. All core protocol features are implemented and functioning, but additions will probably occur as real-world use is explored. There will be bugs. The API and wire-format can be considered relatively stable at the moment, but could change if warranted.
|
||||
|
||||
@@ -91,14 +103,16 @@ Currently, the following interfaces are supported:
|
||||
- Any device with a serial port
|
||||
- TCP over IP networks
|
||||
- UDP over IP networks
|
||||
- External programs via stdio or pipes
|
||||
- Custom hardware via stdio or pipes
|
||||
|
||||
## Development Roadmap
|
||||
- Version 0.3.6
|
||||
- Version 0.3.8
|
||||
- Improving [the manual](https://markqvist.github.io/Reticulum/manual/) with sections specifically for beginners
|
||||
- Utilities for managing identities, signing and encryption
|
||||
- Support for radio and modem interfaces on Android
|
||||
- GUI interface configuration tool
|
||||
- User friendly interface configuration tool
|
||||
- Easy way to share interface configurations, see [#19](https://github.com/markqvist/Reticulum/discussions/19)
|
||||
- Version 0.3.7
|
||||
- More interface types for even broader compatibility
|
||||
- Plain ESP32 devices (ESP-Now, WiFi, Bluetooth, etc.)
|
||||
- More LoRa transceivers
|
||||
@@ -109,10 +123,16 @@ Currently, the following interfaces are supported:
|
||||
- CAN-bus
|
||||
- ZeroMQ
|
||||
- MQTT
|
||||
- IrDA / IrPHY
|
||||
- SPI
|
||||
- i²c
|
||||
- Version 0.3.9
|
||||
- A portable cryptography core, supporting multiple backends
|
||||
- Performance optimisations
|
||||
- Memory optimisations
|
||||
- Planned, but not yet scheduled
|
||||
- Globally routable multicast
|
||||
- Bindings for other programming languages
|
||||
- A portable Reticulum implementation in C, see [#21](https://github.com/markqvist/Reticulum/discussions/21)
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -209,7 +209,7 @@ class Destination:
|
||||
Registers a function to be called when a link has been established to
|
||||
this destination.
|
||||
|
||||
:param callback: A function or method to be called.
|
||||
:param callback: A function or method with the signature *callback(link)* to be called when a new link is established with this destination.
|
||||
"""
|
||||
self.callbacks.link_established = callback
|
||||
|
||||
@@ -218,7 +218,7 @@ class Destination:
|
||||
Registers a function to be called when a packet has been received by
|
||||
this destination.
|
||||
|
||||
:param callback: A function or method to be called.
|
||||
:param callback: A function or method with the signature *callback(data, packet)* to be called when this destination receives a packet.
|
||||
"""
|
||||
self.callbacks.packet = callback
|
||||
|
||||
@@ -228,7 +228,7 @@ class Destination:
|
||||
a packet sent to this destination. Allows control over when and if
|
||||
proofs should be returned for received packets.
|
||||
|
||||
:param callback: A function or method to be called. The callback must return one of True or False. If the callback returns True, a proof will be sent. If it returns False, a proof will not be sent.
|
||||
:param callback: A function or method to with the signature *callback(packet)* be called when a packet that requests a proof is received. The callback must return one of True or False. If the callback returns True, a proof will be sent. If it returns False, a proof will not be sent.
|
||||
"""
|
||||
self.callbacks.proof_requested = callback
|
||||
|
||||
|
||||
@@ -79,6 +79,8 @@ class AX25KISSInterface(Interface):
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
self.pyserial = serial
|
||||
self.serial = None
|
||||
@@ -304,7 +306,7 @@ class AX25KISSInterface(Interface):
|
||||
in_frame = True
|
||||
command = KISS.CMD_UNKNOWN
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU+AX25.HEADER_SIZE):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU+AX25.HEADER_SIZE):
|
||||
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
|
||||
# We only support one HDLC port for now, so
|
||||
# strip off the port nibble
|
||||
|
||||
@@ -60,6 +60,9 @@ class AutoInterface(Interface):
|
||||
self.netifaces = netifaces
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
self.name = name
|
||||
|
||||
+201
-37
@@ -72,10 +72,12 @@ class I2PController:
|
||||
|
||||
self.client_tunnels = {}
|
||||
self.server_tunnels = {}
|
||||
self.i2plib_tunnels = {}
|
||||
self.loop = None
|
||||
self.i2plib = i2plib
|
||||
self.utils = i2plib.utils
|
||||
self.sam_address = i2plib.get_sam_address()
|
||||
self.ready = False
|
||||
|
||||
self.storagepath = rns_storagepath+"/i2p"
|
||||
if not os.path.isdir(self.storagepath):
|
||||
@@ -85,9 +87,20 @@ class I2PController:
|
||||
def start(self):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
self.loop = asyncio.get_event_loop()
|
||||
|
||||
time.sleep(0.10)
|
||||
if self.loop == None:
|
||||
RNS.log("Could not get event loop for "+str(self)+", waiting for event loop to appear", RNS.LOG_VERBOSE)
|
||||
|
||||
while self.loop == None:
|
||||
self.loop = asyncio.get_event_loop()
|
||||
sleep(0.25)
|
||||
|
||||
try:
|
||||
self.ready = True
|
||||
self.loop.run_forever()
|
||||
except Exception as e:
|
||||
self.ready = False
|
||||
RNS.log("Exception on event loop for "+str(self)+": "+str(e), RNS.LOG_ERROR)
|
||||
finally:
|
||||
self.loop.close()
|
||||
@@ -106,6 +119,7 @@ class I2PController:
|
||||
|
||||
def client_tunnel(self, owner, i2p_destination):
|
||||
self.client_tunnels[i2p_destination] = False
|
||||
self.i2plib_tunnels[i2p_destination] = None
|
||||
|
||||
while True:
|
||||
if not self.client_tunnels[i2p_destination]:
|
||||
@@ -113,29 +127,102 @@ class I2PController:
|
||||
async def tunnel_up():
|
||||
RNS.log("Bringing up I2P tunnel to "+str(owner)+", this may take a while...", RNS.LOG_INFO)
|
||||
tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop)
|
||||
self.i2plib_tunnels[i2p_destination] = tunnel
|
||||
await tunnel.run()
|
||||
owner.awaiting_i2p_tunnel = False
|
||||
RNS.log(str(owner)+ " tunnel setup complete", RNS.LOG_VERBOSE)
|
||||
|
||||
try:
|
||||
self.loop.ext_owner = self
|
||||
future = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||
self.client_tunnels[i2p_destination] = True
|
||||
self.loop.ext_owner = self
|
||||
result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||
|
||||
if not i2p_destination in self.i2plib_tunnels:
|
||||
raise IOError("No tunnel control instance was created")
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while setting up I2P tunnel: "+str(e))
|
||||
raise e
|
||||
else:
|
||||
tn = self.i2plib_tunnels[i2p_destination]
|
||||
if tn != None and hasattr(tn, "status"):
|
||||
|
||||
RNS.log("Waiting for status from I2P control process", RNS.LOG_EXTREME)
|
||||
while not tn.status["setup_ran"]:
|
||||
time.sleep(0.1)
|
||||
RNS.log("Got status from I2P control process", RNS.LOG_EXTREME)
|
||||
|
||||
if tn.status["setup_failed"]:
|
||||
raise tn.status["exception"]
|
||||
|
||||
else:
|
||||
self.client_tunnels[i2p_destination] = True
|
||||
owner.awaiting_i2p_tunnel = False
|
||||
if owner.socket != None:
|
||||
if hasattr(owner.socket, "close"):
|
||||
if callable(owner.socket.close):
|
||||
try:
|
||||
owner.socket.shutdown(socket.SHUT_RDWR)
|
||||
except Exception as e:
|
||||
RNS.log("Error while shutting down socket for "+str(owner)+": "+str(e))
|
||||
|
||||
try:
|
||||
owner.socket.close()
|
||||
except Exception as e:
|
||||
RNS.log("Error while closing socket for "+str(owner)+": "+str(e))
|
||||
|
||||
RNS.log(str(owner)+" tunnel setup complete", RNS.LOG_VERBOSE)
|
||||
|
||||
else:
|
||||
raise IOError("Got no status response from SAM API")
|
||||
|
||||
except ConnectionRefusedError as e:
|
||||
raise e
|
||||
|
||||
except ConnectionAbortedError as e:
|
||||
raise e
|
||||
|
||||
except Exception as e:
|
||||
raise IOError("Could not connect to I2P SAM API while configuring to "+str(owner)+". Check that I2P is running and SAM is enabled.")
|
||||
RNS.log("Unexpected error type from I2P SAM: "+str(e), RNS.LOG_ERROR)
|
||||
raise e
|
||||
|
||||
else:
|
||||
i2ptunnel = self.i2plib_tunnels[i2p_destination]
|
||||
if hasattr(i2ptunnel, "status"):
|
||||
# TODO: Remove
|
||||
# RNS.log(str(i2ptunnel.status))
|
||||
i2p_exception = i2ptunnel.status["exception"]
|
||||
|
||||
if i2ptunnel.status["setup_ran"] == False:
|
||||
RNS.log(str(self)+" I2P tunnel setup did not complete", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
elif i2p_exception != None:
|
||||
RNS.log(str(self)+" An error ocurred while setting up I2P tunnel. The contained exception was: "+str(i2p_exception), RNS.LOG_ERROR)
|
||||
RNS.log("Resetting I2P tunnel", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
elif i2ptunnel.status["setup_failed"] == True:
|
||||
RNS.log(str(self)+" Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
else:
|
||||
RNS.log(str(self)+" Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
def server_tunnel(self, owner):
|
||||
i2p_dest_hash = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8")))
|
||||
i2p_keyfile = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash, delimit=False)+".i2p"
|
||||
while RNS.Transport.identity == None:
|
||||
time.sleep(1)
|
||||
|
||||
# Old format
|
||||
i2p_dest_hash_of = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8")))
|
||||
i2p_keyfile_of = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash_of, delimit=False)+".i2p"
|
||||
|
||||
# New format
|
||||
i2p_dest_hash_nf = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8"))+RNS.Identity.full_hash(RNS.Transport.identity.hash))
|
||||
i2p_keyfile_nf = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash_nf, delimit=False)+".i2p"
|
||||
|
||||
# Use old format if a key is already present
|
||||
if os.path.isfile(i2p_keyfile_of):
|
||||
i2p_keyfile = i2p_keyfile_of
|
||||
else:
|
||||
i2p_keyfile = i2p_keyfile_nf
|
||||
|
||||
i2p_dest = None
|
||||
if not os.path.isfile(i2p_keyfile):
|
||||
@@ -154,20 +241,48 @@ class I2PController:
|
||||
owner.b32 = i2p_b32
|
||||
|
||||
self.server_tunnels[i2p_b32] = False
|
||||
self.i2plib_tunnels[i2p_b32] = None
|
||||
|
||||
while self.server_tunnels[i2p_b32] == False:
|
||||
try:
|
||||
async def tunnel_up():
|
||||
RNS.log(str(owner)+" Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
|
||||
tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address)
|
||||
await tunnel.run()
|
||||
RNS.log(str(owner)+ " endpoint setup complete. Now reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE)
|
||||
while True:
|
||||
if self.server_tunnels[i2p_b32] == False:
|
||||
try:
|
||||
async def tunnel_up():
|
||||
RNS.log(str(owner)+" Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
|
||||
tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address)
|
||||
self.i2plib_tunnels[i2p_b32] = tunnel
|
||||
await tunnel.run()
|
||||
RNS.log(str(owner)+ " endpoint setup complete. Now reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE)
|
||||
|
||||
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||
self.server_tunnels[i2p_b32] = True
|
||||
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||
self.server_tunnels[i2p_b32] = True
|
||||
|
||||
except Exception as e:
|
||||
raise IOError("Could not connect to I2P SAM API while configuring "+str(self)+". Check that I2P is running and SAM is enabled.")
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
else:
|
||||
|
||||
i2ptunnel = self.i2plib_tunnels[i2p_b32]
|
||||
if hasattr(i2ptunnel, "status"):
|
||||
# TODO: Remove
|
||||
# RNS.log(str(i2ptunnel.status))
|
||||
i2p_exception = i2ptunnel.status["exception"]
|
||||
|
||||
if i2ptunnel.status["setup_ran"] == False:
|
||||
RNS.log(str(self)+" I2P tunnel setup did not complete", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
elif i2p_exception != None:
|
||||
RNS.log(str(self)+" An error ocurred while setting up I2P tunnel. The contained exception was: "+str(i2p_exception), RNS.LOG_ERROR)
|
||||
RNS.log("Resetting I2P tunnel", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
elif i2ptunnel.status["setup_failed"] == True:
|
||||
RNS.log(str(self)+" Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
else:
|
||||
RNS.log(str(self)+" Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
@@ -191,6 +306,8 @@ class I2PInterfacePeer(Interface):
|
||||
def __init__(self, parent_interface, owner, name, target_i2p_dest=None, connected_socket=None, max_reconnect_tries=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
@@ -212,6 +329,10 @@ class I2PInterfacePeer(Interface):
|
||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||
self.bitrate = I2PInterface.BITRATE_GUESS
|
||||
|
||||
self.announce_rate_target = None
|
||||
self.announce_rate_grace = None
|
||||
self.announce_rate_penalty = None
|
||||
|
||||
if max_reconnect_tries == None:
|
||||
self.max_reconnect_tries = I2PInterfacePeer.RECONNECT_MAX_TRIES
|
||||
else:
|
||||
@@ -233,15 +354,26 @@ class I2PInterfacePeer(Interface):
|
||||
self.initiator = True
|
||||
|
||||
self.bind_ip = "127.0.0.1"
|
||||
self.bind_port = self.parent_interface.i2p.get_free_port()
|
||||
self.local_addr = (self.bind_ip, self.bind_port)
|
||||
self.target_ip = self.bind_ip
|
||||
self.target_port = self.bind_port
|
||||
|
||||
self.awaiting_i2p_tunnel = True
|
||||
|
||||
def tunnel_job():
|
||||
self.parent_interface.i2p.client_tunnel(self, target_i2p_dest)
|
||||
while self.awaiting_i2p_tunnel:
|
||||
try:
|
||||
self.bind_port = self.parent_interface.i2p.get_free_port()
|
||||
self.local_addr = (self.bind_ip, self.bind_port)
|
||||
self.target_ip = self.bind_ip
|
||||
self.target_port = self.bind_port
|
||||
|
||||
if not self.parent_interface.i2p.client_tunnel(self, target_i2p_dest):
|
||||
RNS.log(str(self)+" I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
|
||||
self.awaiting_i2p_tunnel = True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
|
||||
|
||||
time.sleep(15)
|
||||
|
||||
thread = threading.Thread(target=tunnel_job)
|
||||
thread.setDaemon(True)
|
||||
@@ -439,7 +571,7 @@ class I2PInterfacePeer(Interface):
|
||||
in_frame = True
|
||||
command = KISS.CMD_UNKNOWN
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
|
||||
# We only support one HDLC port for now, so
|
||||
# strip off the port nibble
|
||||
@@ -465,7 +597,7 @@ class I2PInterfacePeer(Interface):
|
||||
elif (byte == HDLC.FLAG):
|
||||
in_frame = True
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (byte == HDLC.ESC):
|
||||
escape = True
|
||||
else:
|
||||
@@ -512,7 +644,8 @@ class I2PInterfacePeer(Interface):
|
||||
self.IN = False
|
||||
|
||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||
self.parent_interface.clients -= 1
|
||||
if self.parent_interface.clients > 0:
|
||||
self.parent_interface.clients -= 1
|
||||
|
||||
if self in RNS.Transport.interfaces:
|
||||
if not self.initiator:
|
||||
@@ -526,9 +659,12 @@ class I2PInterfacePeer(Interface):
|
||||
class I2PInterface(Interface):
|
||||
BITRATE_GUESS = 256*1000
|
||||
|
||||
def __init__(self, owner, name, rns_storagepath, peers, connectable = True):
|
||||
def __init__(self, owner, name, rns_storagepath, peers, connectable = False):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.online = False
|
||||
self.clients = 0
|
||||
self.owner = owner
|
||||
@@ -550,10 +686,25 @@ class I2PInterface(Interface):
|
||||
self.address = (self.bind_ip, self.bind_port)
|
||||
self.bitrate = I2PInterface.BITRATE_GUESS
|
||||
|
||||
self.online = False
|
||||
|
||||
i2p_thread = threading.Thread(target=self.i2p.start)
|
||||
i2p_thread.setDaemon(True)
|
||||
i2p_thread.start()
|
||||
|
||||
i2p_notready_warning = False
|
||||
time.sleep(0.25)
|
||||
|
||||
if not self.i2p.ready:
|
||||
RNS.log("I2P controller did not become available in time, waiting for controller", RNS.LOG_VERBOSE)
|
||||
i2p_notready_warning = True
|
||||
|
||||
while not self.i2p.ready:
|
||||
time.sleep(0.25)
|
||||
|
||||
if i2p_notready_warning == True:
|
||||
RNS.log("I2P controller ready, continuing setup", RNS.LOG_VERBOSE)
|
||||
|
||||
def handlerFactory(callback):
|
||||
def createHandler(*args, **keys):
|
||||
return I2PInterfaceHandler(callback, *args, **keys)
|
||||
@@ -568,7 +719,18 @@ class I2PInterface(Interface):
|
||||
|
||||
if self.connectable:
|
||||
def tunnel_job():
|
||||
self.i2p.server_tunnel(self)
|
||||
while True:
|
||||
try:
|
||||
if not self.i2p.server_tunnel(self):
|
||||
RNS.log(str(self)+" I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
|
||||
self.online = False
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
|
||||
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
thread = threading.Thread(target=tunnel_job)
|
||||
thread.setDaemon(True)
|
||||
@@ -576,7 +738,7 @@ class I2PInterface(Interface):
|
||||
|
||||
if peers != None:
|
||||
for peer_addr in peers:
|
||||
interface_name = peer_addr
|
||||
interface_name = self.name+" to "+peer_addr
|
||||
peer_interface = I2PInterfacePeer(self, self.owner, interface_name, peer_addr)
|
||||
peer_interface.OUT = True
|
||||
peer_interface.IN = True
|
||||
@@ -584,9 +746,6 @@ class I2PInterface(Interface):
|
||||
peer_interface.parent_count = False
|
||||
RNS.Transport.interfaces.append(peer_interface)
|
||||
|
||||
self.online = True
|
||||
|
||||
|
||||
def incoming_connection(self, handler):
|
||||
RNS.log("Accepting incoming I2P connection", RNS.LOG_VERBOSE)
|
||||
interface_name = "Connected peer on "+self.name
|
||||
@@ -599,6 +758,11 @@ class I2PInterface(Interface):
|
||||
spawned_interface.ifac_size = self.ifac_size
|
||||
spawned_interface.ifac_netname = self.ifac_netname
|
||||
spawned_interface.ifac_netkey = self.ifac_netkey
|
||||
spawned_interface.announce_rate_target = self.announce_rate_target
|
||||
spawned_interface.announce_rate_grace = self.announce_rate_grace
|
||||
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
|
||||
spawned_interface.mode = self.mode
|
||||
spawned_interface.HW_MTU = self.HW_MTU
|
||||
RNS.log("Spawned new I2PInterface Peer: "+str(spawned_interface), RNS.LOG_VERBOSE)
|
||||
RNS.Transport.interfaces.append(spawned_interface)
|
||||
self.clients += 1
|
||||
|
||||
@@ -31,9 +31,17 @@ class Interface:
|
||||
RPT = False
|
||||
name = None
|
||||
|
||||
# Interface mode definitions
|
||||
MODE_FULL = 0x01
|
||||
MODE_POINT_TO_POINT = 0x02
|
||||
MODE_ACCESS_POINT = 0x03
|
||||
MODE_ROAMING = 0x04
|
||||
MODE_BOUNDARY = 0x05
|
||||
MODE_GATEWAY = 0x06
|
||||
|
||||
# Which interface modes a Transport Node
|
||||
# should actively discover paths for.
|
||||
DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
||||
|
||||
def __init__(self):
|
||||
self.rxb = 0
|
||||
@@ -56,7 +64,8 @@ class Interface:
|
||||
stale.append(a)
|
||||
|
||||
for s in stale:
|
||||
self.announce_queue.remove(s)
|
||||
if s in self.announce_queue:
|
||||
self.announce_queue.remove(s)
|
||||
|
||||
if len(self.announce_queue) > 0:
|
||||
min_hops = min(entry["hops"] for entry in self.announce_queue)
|
||||
@@ -70,7 +79,10 @@ class Interface:
|
||||
self.announce_allowed_at = now + wait_time
|
||||
|
||||
self.processOutgoing(selected["raw"])
|
||||
self.announce_queue.remove(selected)
|
||||
|
||||
if selected in self.announce_queue:
|
||||
self.announce_queue.remove(selected)
|
||||
|
||||
if len(self.announce_queue) > 0:
|
||||
timer = threading.Timer(wait_time, self.process_announce_queue)
|
||||
timer.start()
|
||||
|
||||
@@ -73,6 +73,8 @@ class KISSInterface(Interface):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
if beacon_data == None:
|
||||
beacon_data = ""
|
||||
|
||||
@@ -279,7 +281,7 @@ class KISSInterface(Interface):
|
||||
in_frame = True
|
||||
command = KISS.CMD_UNKNOWN
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
|
||||
# We only support one HDLC port for now, so
|
||||
# strip off the port nibble
|
||||
|
||||
@@ -49,6 +49,9 @@ class LocalClientInterface(Interface):
|
||||
def __init__(self, owner, name, target_port = None, connected_socket=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.online = False
|
||||
|
||||
self.IN = True
|
||||
@@ -80,6 +83,10 @@ class LocalClientInterface(Interface):
|
||||
self.online = True
|
||||
self.writing = False
|
||||
|
||||
self.announce_rate_target = None
|
||||
self.announce_rate_grace = None
|
||||
self.announce_rate_penalty = None
|
||||
|
||||
if connected_socket == None:
|
||||
thread = threading.Thread(target=self.read_loop)
|
||||
thread.setDaemon(True)
|
||||
@@ -173,7 +180,7 @@ class LocalClientInterface(Interface):
|
||||
elif (byte == HDLC.FLAG):
|
||||
in_frame = True
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (byte == HDLC.ESC):
|
||||
escape = True
|
||||
else:
|
||||
@@ -285,6 +292,10 @@ class LocalServerInterface(Interface):
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
self.announce_rate_target = None
|
||||
self.announce_rate_grace = None
|
||||
self.announce_rate_penalty = None
|
||||
|
||||
self.bitrate = 1000*1000*1000
|
||||
self.online = True
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 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.
|
||||
|
||||
from .Interface import Interface
|
||||
from time import sleep
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import RNS
|
||||
|
||||
import subprocess
|
||||
import shlex
|
||||
|
||||
class HDLC():
|
||||
# The Pipe Interface packetizes data using
|
||||
# simplified HDLC framing, similar to PPP
|
||||
FLAG = 0x7E
|
||||
ESC = 0x7D
|
||||
ESC_MASK = 0x20
|
||||
|
||||
@staticmethod
|
||||
def escape(data):
|
||||
data = data.replace(bytes([HDLC.ESC]), bytes([HDLC.ESC, HDLC.ESC^HDLC.ESC_MASK]))
|
||||
data = data.replace(bytes([HDLC.FLAG]), bytes([HDLC.ESC, HDLC.FLAG^HDLC.ESC_MASK]))
|
||||
return data
|
||||
|
||||
class PipeInterface(Interface):
|
||||
MAX_CHUNK = 32768
|
||||
BITRATE_GUESS = 1*1000*1000
|
||||
|
||||
owner = None
|
||||
command = None
|
||||
|
||||
def __init__(self, owner, name, command, respawn_delay):
|
||||
if respawn_delay == None:
|
||||
respawn_delay = 5
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.owner = owner
|
||||
self.name = name
|
||||
self.command = command
|
||||
self.process = None
|
||||
self.timeout = 100
|
||||
self.online = False
|
||||
self.pipe_is_open = False
|
||||
self.bitrate = PipeInterface.BITRATE_GUESS
|
||||
self.respawn_delay = respawn_delay
|
||||
|
||||
try:
|
||||
self.open_pipe()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could connect pipe for interface "+str(self), RNS.LOG_ERROR)
|
||||
raise e
|
||||
|
||||
if self.pipe_is_open:
|
||||
self.configure_pipe()
|
||||
else:
|
||||
raise IOError("Could not connect pipe")
|
||||
|
||||
|
||||
def open_pipe(self):
|
||||
RNS.log("Connecting subprocess pipe for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
|
||||
try:
|
||||
self.process = subprocess.Popen(shlex.split(self.command), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
self.pipe_is_open = True
|
||||
except Exception as e:
|
||||
raise e
|
||||
self.pipe_is_open = False
|
||||
|
||||
|
||||
def configure_pipe(self):
|
||||
sleep(0.01)
|
||||
thread = threading.Thread(target=self.readLoop)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
self.online = True
|
||||
RNS.log("Subprocess pipe for "+str(self)+" is now connected", RNS.LOG_VERBOSE)
|
||||
|
||||
|
||||
def processIncoming(self, data):
|
||||
self.rxb += len(data)
|
||||
self.owner.inbound(data, self)
|
||||
|
||||
|
||||
def processOutgoing(self,data):
|
||||
if self.online:
|
||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||
written = self.process.stdin.write(data)
|
||||
self.process.stdin.flush()
|
||||
self.txb += len(data)
|
||||
if written != len(data):
|
||||
raise IOError("Pipe interface only wrote "+str(written)+" bytes of "+str(len(data)))
|
||||
|
||||
|
||||
def readLoop(self):
|
||||
try:
|
||||
in_frame = False
|
||||
escape = False
|
||||
data_buffer = b""
|
||||
last_read_ms = int(time.time()*1000)
|
||||
|
||||
while True:
|
||||
process_output = self.process.stdout.read(1)
|
||||
if len(process_output) == 0 and self.process.poll() is not None:
|
||||
break
|
||||
|
||||
else:
|
||||
byte = ord(process_output)
|
||||
last_read_ms = int(time.time()*1000)
|
||||
|
||||
if (in_frame and byte == HDLC.FLAG):
|
||||
in_frame = False
|
||||
self.processIncoming(data_buffer)
|
||||
elif (byte == HDLC.FLAG):
|
||||
in_frame = True
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (byte == HDLC.ESC):
|
||||
escape = True
|
||||
else:
|
||||
if (escape):
|
||||
if (byte == HDLC.FLAG ^ HDLC.ESC_MASK):
|
||||
byte = HDLC.FLAG
|
||||
if (byte == HDLC.ESC ^ HDLC.ESC_MASK):
|
||||
byte = HDLC.ESC
|
||||
escape = False
|
||||
data_buffer = data_buffer+bytes([byte])
|
||||
|
||||
RNS.log("Subprocess terminated on "+str(self))
|
||||
self.process.kill()
|
||||
|
||||
except Exception as e:
|
||||
self.online = False
|
||||
try:
|
||||
self.process.kill()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
RNS.log("A pipe error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||
|
||||
if RNS.Reticulum.panic_on_interface_error:
|
||||
RNS.panic()
|
||||
|
||||
RNS.log("Reticulum will attempt to reconnect the interface periodically.", RNS.LOG_ERROR)
|
||||
|
||||
self.online = False
|
||||
self.reconnect_pipe()
|
||||
|
||||
def reconnect_pipe(self):
|
||||
while not self.online:
|
||||
try:
|
||||
time.sleep(self.respawn_delay)
|
||||
RNS.log("Attempting to respawn subprocess for "+str(self)+"...", RNS.LOG_VERBOSE)
|
||||
self.open_pipe()
|
||||
if self.pipe_is_open:
|
||||
self.configure_pipe()
|
||||
except Exception as e:
|
||||
RNS.log("Error while spawning subprocess, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
RNS.log("Reconnected pipe for "+str(self))
|
||||
|
||||
def __str__(self):
|
||||
return "PipeInterface["+self.name+"]"
|
||||
@@ -111,6 +111,8 @@ class RNodeInterface(Interface):
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 508
|
||||
|
||||
self.pyserial = serial
|
||||
self.serial = None
|
||||
@@ -439,7 +441,7 @@ class RNodeInterface(Interface):
|
||||
command = KISS.CMD_UNKNOWN
|
||||
data_buffer = b""
|
||||
command_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
|
||||
command = byte
|
||||
elif (command == KISS.CMD_DATA):
|
||||
|
||||
@@ -62,6 +62,8 @@ class SerialInterface(Interface):
|
||||
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 564
|
||||
|
||||
self.pyserial = serial
|
||||
self.serial = None
|
||||
@@ -117,7 +119,7 @@ class SerialInterface(Interface):
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
self.online = True
|
||||
RNS.log("Serial port "+self.port+" is now open")
|
||||
RNS.log("Serial port "+self.port+" is now open", RNS.LOG_VERBOSE)
|
||||
|
||||
|
||||
def processIncoming(self, data):
|
||||
@@ -152,7 +154,7 @@ class SerialInterface(Interface):
|
||||
elif (byte == HDLC.FLAG):
|
||||
in_frame = True
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (byte == HDLC.ESC):
|
||||
escape = True
|
||||
else:
|
||||
|
||||
@@ -79,6 +79,8 @@ class TCPClientInterface(Interface):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
self.socket = None
|
||||
@@ -293,7 +295,7 @@ class TCPClientInterface(Interface):
|
||||
in_frame = True
|
||||
command = KISS.CMD_UNKNOWN
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
|
||||
# We only support one HDLC port for now, so
|
||||
# strip off the port nibble
|
||||
@@ -319,7 +321,7 @@ class TCPClientInterface(Interface):
|
||||
elif (byte == HDLC.FLAG):
|
||||
in_frame = True
|
||||
data_buffer = b""
|
||||
elif (in_frame and len(data_buffer) < RNS.Reticulum.MTU):
|
||||
elif (in_frame and len(data_buffer) < self.HW_MTU):
|
||||
if (byte == HDLC.ESC):
|
||||
escape = True
|
||||
else:
|
||||
@@ -405,6 +407,9 @@ class TCPServerInterface(Interface):
|
||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.online = False
|
||||
self.clients = 0
|
||||
|
||||
@@ -456,6 +461,11 @@ class TCPServerInterface(Interface):
|
||||
spawned_interface.ifac_size = self.ifac_size
|
||||
spawned_interface.ifac_netname = self.ifac_netname
|
||||
spawned_interface.ifac_netkey = self.ifac_netkey
|
||||
spawned_interface.announce_rate_target = self.announce_rate_target
|
||||
spawned_interface.announce_rate_grace = self.announce_rate_grace
|
||||
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
|
||||
spawned_interface.mode = self.mode
|
||||
spawned_interface.HW_MTU = self.HW_MTU
|
||||
spawned_interface.online = True
|
||||
RNS.log("Spawned new TCPClient Interface: "+str(spawned_interface), RNS.LOG_VERBOSE)
|
||||
RNS.Transport.interfaces.append(spawned_interface)
|
||||
|
||||
@@ -57,6 +57,9 @@ class UDPInterface(Interface):
|
||||
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
|
||||
self.rxb = 0
|
||||
self.txb = 0
|
||||
|
||||
self.HW_MTU = 1064
|
||||
|
||||
self.IN = True
|
||||
self.OUT = False
|
||||
self.name = name
|
||||
|
||||
+54
-18
@@ -71,16 +71,30 @@ class Link:
|
||||
|
||||
ESTABLISHMENT_TIMEOUT_PER_HOP = RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||
"""
|
||||
Default timeout for link establishment in seconds per hop to destination.
|
||||
Timeout for link establishment in seconds per hop to destination.
|
||||
"""
|
||||
|
||||
TRAFFIC_TIMEOUT_FACTOR = 6
|
||||
KEEPALIVE_TIMEOUT_FACTOR = 4
|
||||
"""
|
||||
RTT timeout factor used in link timeout calculation.
|
||||
"""
|
||||
STALE_GRACE = 2
|
||||
"""
|
||||
Grace period in seconds used in link timeout calculation.
|
||||
"""
|
||||
KEEPALIVE = 360
|
||||
"""
|
||||
Interval for sending keep-alive packets on established links in seconds.
|
||||
"""
|
||||
STALE_TIME = 2*KEEPALIVE
|
||||
"""
|
||||
If no traffic or keep-alive packets are received within this period, the
|
||||
link will be marked as stale, and a final keep-alive packet will be sent.
|
||||
If after this no traffic or keep-alive packets are received within ``RTT`` *
|
||||
``KEEPALIVE_TIMEOUT_FACTOR`` + ``STALE_GRACE``, the link is considered timed out,
|
||||
and will be torn down.
|
||||
"""
|
||||
|
||||
PENDING = 0x00
|
||||
HANDSHAKE = 0x01
|
||||
@@ -145,6 +159,7 @@ class Link:
|
||||
self.traffic_timeout_factor = Link.TRAFFIC_TIMEOUT_FACTOR
|
||||
self.keepalive_timeout_factor = Link.KEEPALIVE_TIMEOUT_FACTOR
|
||||
self.keepalive = Link.KEEPALIVE
|
||||
self.stale_time = Link.STALE_TIME
|
||||
self.watchdog_lock = False
|
||||
self.status = Link.PENDING
|
||||
self.activated_at = None
|
||||
@@ -267,7 +282,7 @@ class Link:
|
||||
self.attached_interface = packet.receiving_interface
|
||||
self.__remote_identity = self.destination.identity
|
||||
RNS.Transport.activate_link(self)
|
||||
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(self.rtt), RNS.LOG_VERBOSE)
|
||||
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE)
|
||||
rtt_data = umsgpack.packb(self.rtt)
|
||||
rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT)
|
||||
rtt_packet.send()
|
||||
@@ -334,7 +349,8 @@ class Link:
|
||||
response_callback = response_callback,
|
||||
failed_callback = failed_callback,
|
||||
progress_callback = progress_callback,
|
||||
timeout = timeout
|
||||
timeout = timeout,
|
||||
request_size = len(packed_request),
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -348,7 +364,8 @@ class Link:
|
||||
response_callback = response_callback,
|
||||
failed_callback = failed_callback,
|
||||
progress_callback = progress_callback,
|
||||
timeout = timeout
|
||||
timeout = timeout,
|
||||
request_size = len(packed_request),
|
||||
)
|
||||
|
||||
|
||||
@@ -383,7 +400,9 @@ class Link:
|
||||
"""
|
||||
:returns: The time in seconds since last inbound packet on the link.
|
||||
"""
|
||||
return time.time() - self.last_inbound
|
||||
activated_at = self.activated_at if self.activated_at != None else 0
|
||||
last_inbound = max(self.last_inbound, activated_at)
|
||||
return time.time() - last_inbound
|
||||
|
||||
def no_outbound_for(self):
|
||||
"""
|
||||
@@ -497,13 +516,21 @@ class Link:
|
||||
sleep_time = 0.001
|
||||
|
||||
elif self.status == Link.ACTIVE:
|
||||
if time.time() >= self.last_inbound + self.keepalive:
|
||||
sleep_time = self.rtt * self.keepalive_timeout_factor + Link.STALE_GRACE
|
||||
self.status = Link.STALE
|
||||
activated_at = self.activated_at if self.activated_at != None else 0
|
||||
last_inbound = max(self.last_inbound, activated_at)
|
||||
|
||||
if time.time() >= last_inbound + self.keepalive:
|
||||
if self.initiator:
|
||||
self.send_keepalive()
|
||||
|
||||
if time.time() >= last_inbound + self.stale_time:
|
||||
sleep_time = self.rtt * self.keepalive_timeout_factor + Link.STALE_GRACE
|
||||
self.status = Link.STALE
|
||||
else:
|
||||
sleep_time = self.keepalive
|
||||
|
||||
else:
|
||||
sleep_time = (self.last_inbound + self.keepalive) - time.time()
|
||||
sleep_time = (last_inbound + self.keepalive) - time.time()
|
||||
|
||||
elif self.status == Link.STALE:
|
||||
sleep_time = 0.001
|
||||
@@ -543,7 +570,7 @@ class Link:
|
||||
allowed = False
|
||||
if not allow == RNS.Destination.ALLOW_NONE:
|
||||
if allow == RNS.Destination.ALLOW_LIST:
|
||||
if self.__remote_identity.hash in allowed_list:
|
||||
if self.__remote_identity != None and self.__remote_identity.hash in allowed_list:
|
||||
allowed = True
|
||||
elif allow == RNS.Destination.ALLOW_ALL:
|
||||
allowed = True
|
||||
@@ -650,7 +677,7 @@ class Link:
|
||||
self.__remote_identity = identity
|
||||
if self.callbacks.remote_identified != None:
|
||||
try:
|
||||
self.callbacks.remote_identified(self.__remote_identity)
|
||||
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)
|
||||
|
||||
@@ -687,19 +714,21 @@ class Link:
|
||||
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.get_request_id(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.get_size(packet)
|
||||
pending_request.response_transfer_size = RNS.ResourceAdvertisement.get_transfer_size(packet)
|
||||
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:
|
||||
if self.callbacks.resource(resource):
|
||||
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)
|
||||
@@ -812,6 +841,12 @@ class Link:
|
||||
self.callbacks.link_established = callback
|
||||
|
||||
def set_link_closed_callback(self, callback):
|
||||
"""
|
||||
Registers a function to be called when a link has been
|
||||
torn down.
|
||||
|
||||
:param callback: A function or method with the signature *callback(link)* to be called.
|
||||
"""
|
||||
self.callbacks.link_closed = callback
|
||||
|
||||
def set_packet_callback(self, callback):
|
||||
@@ -830,7 +865,7 @@ class Link:
|
||||
the resource will be accepted. If it returns *False* it will
|
||||
be ignored.
|
||||
|
||||
:param callback: A function or method with the signature *callback(resource)* to be called.
|
||||
:param callback: A function or method with the signature *callback(resource)* to be called. Please note that only the basic information of the resource is available at this time, such as *get_transfer_size()*, *get_data_size()*, *get_parts()* and *is_compressed()*.
|
||||
"""
|
||||
self.callbacks.resource = callback
|
||||
|
||||
@@ -857,7 +892,7 @@ class Link:
|
||||
Registers a function to be called when an initiating peer has
|
||||
identified over this link.
|
||||
|
||||
:param callback: A function or method with the signature *callback(identity)* to be called.
|
||||
:param callback: A function or method with the signature *callback(link, identity)* to be called.
|
||||
"""
|
||||
self.callbacks.remote_identified = callback
|
||||
|
||||
@@ -920,7 +955,7 @@ class RequestReceipt():
|
||||
RECEIVING = 0x03
|
||||
READY = 0x04
|
||||
|
||||
def __init__(self, link, packet_receipt = None, resource = None, response_callback = None, failed_callback = None, progress_callback = None, timeout = None):
|
||||
def __init__(self, link, packet_receipt = None, resource = None, response_callback = None, failed_callback = None, progress_callback = None, timeout = None, request_size = None):
|
||||
self.packet_receipt = packet_receipt
|
||||
self.resource = resource
|
||||
self.started_at = None
|
||||
@@ -936,6 +971,7 @@ class RequestReceipt():
|
||||
|
||||
self.link = link
|
||||
self.request_id = self.hash
|
||||
self.request_size = request_size
|
||||
|
||||
self.response = None
|
||||
self.response_transfer_size = None
|
||||
|
||||
+62
-6
@@ -526,8 +526,11 @@ class Resource:
|
||||
RNS.log("Error while executing resource assembled callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
try:
|
||||
self.data.close()
|
||||
if hasattr(self.data, "close") and callable(self.data.close):
|
||||
self.data.close()
|
||||
|
||||
os.unlink(self.storagepath)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR)
|
||||
RNS.log(str(e))
|
||||
@@ -564,7 +567,7 @@ class Resource:
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
@@ -826,8 +829,44 @@ class Resource:
|
||||
progress = self.processed_parts / self.progress_total_parts
|
||||
return progress
|
||||
|
||||
def get_transfer_size(self):
|
||||
"""
|
||||
:returns: The number of bytes needed to transfer the resource.
|
||||
"""
|
||||
return self.size
|
||||
|
||||
def get_data_size(self):
|
||||
"""
|
||||
:returns: The total data size of the resource.
|
||||
"""
|
||||
return self.total_size
|
||||
|
||||
def get_parts(self):
|
||||
"""
|
||||
:returns: The number of parts the resource will be transferred in.
|
||||
"""
|
||||
return self.total_parts
|
||||
|
||||
def get_segments(self):
|
||||
"""
|
||||
:returns: The number of segments the resource is divided into.
|
||||
"""
|
||||
return self.total_segments
|
||||
|
||||
def get_hash(self):
|
||||
"""
|
||||
:returns: The hash of the resource.
|
||||
"""
|
||||
return self.hash
|
||||
|
||||
def is_compressed(self):
|
||||
"""
|
||||
:returns: Whether the resource is compressed.
|
||||
"""
|
||||
return self.compressed
|
||||
|
||||
def __str__(self):
|
||||
return "<"+RNS.hexrep(self.hash)+"/"+RNS.hexrep(self.link.link_id)+">"
|
||||
return "<"+RNS.hexrep(self.hash,delimit=False)+"/"+RNS.hexrep(self.link.link_id,delimit=False)+">"
|
||||
|
||||
|
||||
class ResourceAdvertisement:
|
||||
@@ -857,19 +896,19 @@ class ResourceAdvertisement:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_request_id(advertisement_packet):
|
||||
def read_request_id(advertisement_packet):
|
||||
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
|
||||
return adv.q
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_transfer_size(advertisement_packet):
|
||||
def read_transfer_size(advertisement_packet):
|
||||
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
|
||||
return adv.t
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_size(advertisement_packet):
|
||||
def read_size(advertisement_packet):
|
||||
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
|
||||
return adv.d
|
||||
|
||||
@@ -903,6 +942,23 @@ class ResourceAdvertisement:
|
||||
# Flags
|
||||
self.f = 0x00 | self.p << 4 | self.u << 3 | self.s << 2 | self.c << 1 | self.e
|
||||
|
||||
def get_transfer_size(self):
|
||||
return self.t
|
||||
|
||||
def get_data_size(self):
|
||||
return self.d
|
||||
|
||||
def get_parts(self):
|
||||
return self.n
|
||||
|
||||
def get_segments(self):
|
||||
return self.l
|
||||
|
||||
def get_hash(self):
|
||||
return self.h
|
||||
|
||||
def is_compressed(self):
|
||||
return self.c
|
||||
|
||||
def pack(self, segment=0):
|
||||
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
|
||||
|
||||
+122
-7
@@ -181,6 +181,7 @@ class Reticulum:
|
||||
Reticulum.storagepath = Reticulum.configdir+"/storage"
|
||||
Reticulum.cachepath = Reticulum.configdir+"/storage/cache"
|
||||
Reticulum.resourcepath = Reticulum.configdir+"/storage/resources"
|
||||
Reticulum.identitypath = Reticulum.configdir+"/storage/identities"
|
||||
|
||||
Reticulum.__transport_enabled = False
|
||||
Reticulum.__use_implicit_proof = True
|
||||
@@ -216,6 +217,9 @@ class Reticulum:
|
||||
if not os.path.isdir(Reticulum.resourcepath):
|
||||
os.makedirs(Reticulum.resourcepath)
|
||||
|
||||
if not os.path.isdir(Reticulum.identitypath):
|
||||
os.makedirs(Reticulum.identitypath)
|
||||
|
||||
if os.path.isfile(self.configpath):
|
||||
try:
|
||||
self.config = ConfigObj(self.configpath)
|
||||
@@ -275,7 +279,7 @@ class Reticulum:
|
||||
self.is_standalone_instance = False
|
||||
self.is_connected_to_shared_instance = True
|
||||
Reticulum.__transport_enabled = False
|
||||
RNS.log("Connected to local shared instance via: "+str(interface), RNS.LOG_DEBUG)
|
||||
RNS.log("Connected to locally available Reticulum instance via: "+str(interface), RNS.LOG_DEBUG)
|
||||
except Exception as e:
|
||||
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
@@ -328,7 +332,7 @@ class Reticulum:
|
||||
self.__start_local_interface()
|
||||
|
||||
if self.is_shared_instance or self.is_standalone_instance:
|
||||
RNS.log("Bringing up system interfaces...", RNS.LOG_DEBUG)
|
||||
RNS.log("Bringing up system interfaces...", RNS.LOG_VERBOSE)
|
||||
interface_names = []
|
||||
for name in self.config["interfaces"]:
|
||||
if not name in interface_names:
|
||||
@@ -343,20 +347,34 @@ class Reticulum:
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
if "interface_mode" in c:
|
||||
c["interface_mode"] = str(c["interface_mode"]).lower()
|
||||
if c["interface_mode"] == "full":
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
elif c["interface_mode"] == "access_point" or c["interface_mode"] == "accesspoint" or c["interface_mode"] == "ap":
|
||||
interface_mode = Interface.Interface.MODE_ACCESS_POINT
|
||||
elif c["interface_mode"] == "pointtopoint" or c["interface_mode"] == "ptp":
|
||||
interface_mode = Interface.Interface.MODE_POINT_TO_POINT
|
||||
elif c["interface_mode"] == "roaming":
|
||||
interface_mode = Interface.Interface.MODE_ROAMING
|
||||
elif c["interface_mode"] == "boundary":
|
||||
interface_mode = Interface.Interface.MODE_BOUNDARY
|
||||
elif c["mode"] == "gateway" or c["mode"] == "gw":
|
||||
interface_mode = Interface.Interface.MODE_GATEWAY
|
||||
|
||||
elif "mode" in c:
|
||||
c["mode"] = str(c["mode"]).lower()
|
||||
if c["mode"] == "full":
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
elif c["mode"] == "access_point" or c["mode"] == "accesspoint" or c["mode"] == "ap":
|
||||
interface_mode = Interface.Interface.MODE_ACCESS_POINT
|
||||
elif c["mode"] == "pointtopoint" or c["mode"] == "ptp":
|
||||
interface_mode = Interface.Interface.MODE_POINT_TO_POINT
|
||||
elif c["mode"] == "roaming":
|
||||
interface_mode = Interface.Interface.MODE_ROAMING
|
||||
elif c["mode"] == "boundary":
|
||||
interface_mode = Interface.Interface.MODE_BOUNDARY
|
||||
elif c["mode"] == "gateway" or c["mode"] == "gw":
|
||||
interface_mode = Interface.Interface.MODE_GATEWAY
|
||||
|
||||
ifac_size = None
|
||||
if "ifac_size" in c:
|
||||
@@ -383,7 +401,28 @@ class Reticulum:
|
||||
if "bitrate" in c:
|
||||
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
|
||||
configured_bitrate = c.as_int("bitrate")
|
||||
|
||||
announce_rate_target = None
|
||||
if "announce_rate_target" in c:
|
||||
if c.as_int("announce_rate_target") > 0:
|
||||
announce_rate_target = c.as_int("announce_rate_target")
|
||||
|
||||
announce_rate_grace = None
|
||||
if "announce_rate_grace" in c:
|
||||
if c.as_int("announce_rate_grace") >= 0:
|
||||
announce_rate_grace = c.as_int("announce_rate_grace")
|
||||
|
||||
announce_rate_penalty = None
|
||||
if "announce_rate_penalty" in c:
|
||||
if c.as_int("announce_rate_penalty") >= 0:
|
||||
announce_rate_penalty = c.as_int("announce_rate_penalty")
|
||||
|
||||
if announce_rate_target != None and announce_rate_grace == None:
|
||||
announce_rate_grace = 0
|
||||
|
||||
if announce_rate_target != None and announce_rate_penalty == None:
|
||||
announce_rate_penalty = 0
|
||||
|
||||
announce_cap = Reticulum.ANNOUNCE_CAP/100.0
|
||||
if "announce_cap" in c:
|
||||
if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
|
||||
@@ -496,7 +535,7 @@ class Reticulum:
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
if interface_mode != Interface.Interface.MODE_FULL:
|
||||
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
@@ -531,7 +570,7 @@ class Reticulum:
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
if interface_mode != Interface.Interface.MODE_FULL:
|
||||
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
@@ -562,7 +601,7 @@ class Reticulum:
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
if interface_mode != Interface.Interface.MODE_FULL:
|
||||
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||
interface_mode = Interface.Interface.MODE_FULL
|
||||
|
||||
@@ -611,6 +650,35 @@ class Reticulum:
|
||||
else:
|
||||
interface.ifac_size = 8
|
||||
|
||||
if c["type"] == "PipeInterface":
|
||||
command = c["command"] if "command" in c else None
|
||||
respawn_delay = c.as_float("respawn_delay") if "respawn_delay" in c else None
|
||||
|
||||
if command == None:
|
||||
raise ValueError("No command specified for PipeInterface")
|
||||
|
||||
interface = PipeInterface.PipeInterface(
|
||||
RNS.Transport,
|
||||
name,
|
||||
command,
|
||||
respawn_delay,
|
||||
)
|
||||
|
||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||
interface.OUT = False
|
||||
else:
|
||||
interface.OUT = True
|
||||
|
||||
interface.mode = interface_mode
|
||||
|
||||
interface.announce_cap = announce_cap
|
||||
if configured_bitrate:
|
||||
interface.bitrate = configured_bitrate
|
||||
if ifac_size != None:
|
||||
interface.ifac_size = ifac_size
|
||||
else:
|
||||
interface.ifac_size = 8
|
||||
|
||||
if c["type"] == "KISSInterface":
|
||||
preamble = int(c["preamble"]) if "preamble" in c else None
|
||||
txtail = int(c["txtail"]) if "txtail" in c else None
|
||||
@@ -755,6 +823,10 @@ class Reticulum:
|
||||
interface.ifac_size = 8
|
||||
|
||||
if interface != None:
|
||||
interface.announce_rate_target = announce_rate_target
|
||||
interface.announce_rate_grace = announce_rate_grace
|
||||
interface.announce_rate_penalty = announce_rate_penalty
|
||||
|
||||
interface.ifac_netname = ifac_netname
|
||||
interface.ifac_netkey = ifac_netkey
|
||||
|
||||
@@ -792,7 +864,7 @@ class Reticulum:
|
||||
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
|
||||
RNS.panic()
|
||||
|
||||
RNS.log("System interfaces are ready", RNS.LOG_DEBUG)
|
||||
RNS.log("System interfaces are ready", RNS.LOG_VERBOSE)
|
||||
|
||||
|
||||
|
||||
@@ -819,6 +891,9 @@ class Reticulum:
|
||||
if path == "path_table":
|
||||
rpc_connection.send(self.get_path_table())
|
||||
|
||||
if path == "rate_table":
|
||||
rpc_connection.send(self.get_rate_table())
|
||||
|
||||
if path == "next_hop_if_name":
|
||||
rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"]))
|
||||
|
||||
@@ -837,6 +912,9 @@ class Reticulum:
|
||||
if path == "path":
|
||||
rpc_connection.send(self.drop_path(call["destination_hash"]))
|
||||
|
||||
if path == "announce_queues":
|
||||
rpc_connection.send(self.drop_announce_queues())
|
||||
|
||||
rpc_connection.close()
|
||||
|
||||
except Exception as e:
|
||||
@@ -858,6 +936,12 @@ class Reticulum:
|
||||
else:
|
||||
ifstats["clients"] = None
|
||||
|
||||
if hasattr(interface, "i2p") and hasattr(interface, "connectable"):
|
||||
if interface.connectable:
|
||||
ifstats["i2p_connectable"] = True
|
||||
else:
|
||||
ifstats["i2p_connectable"] = False
|
||||
|
||||
if hasattr(interface, "b32"):
|
||||
if interface.b32 != None:
|
||||
ifstats["i2p_b32"] = interface.b32+".b32.i2p"
|
||||
@@ -928,6 +1012,27 @@ class Reticulum:
|
||||
|
||||
return path_table
|
||||
|
||||
def get_rate_table(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
rpc_connection.send({"get": "rate_table"})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else:
|
||||
rate_table = []
|
||||
for dst_hash in RNS.Transport.announce_rate_table:
|
||||
entry = {
|
||||
"hash": dst_hash,
|
||||
"last": RNS.Transport.announce_rate_table[dst_hash]["last"],
|
||||
"rate_violations": RNS.Transport.announce_rate_table[dst_hash]["rate_violations"],
|
||||
"blocked_until": RNS.Transport.announce_rate_table[dst_hash]["blocked_until"],
|
||||
"timestamps": RNS.Transport.announce_rate_table[dst_hash]["timestamps"],
|
||||
}
|
||||
rate_table.append(entry)
|
||||
|
||||
return rate_table
|
||||
|
||||
def drop_path(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
@@ -938,6 +1043,16 @@ class Reticulum:
|
||||
else:
|
||||
return RNS.Transport.expire_path(destination)
|
||||
|
||||
def drop_announce_queues(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
rpc_connection.send({"drop": "announce_queues"})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else:
|
||||
return RNS.Transport.drop_announce_queues()
|
||||
|
||||
def get_next_hop_if_name(self, destination):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
@@ -1096,6 +1211,6 @@ loglevel = 4
|
||||
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
enabled = Yes
|
||||
|
||||
'''.splitlines()
|
||||
|
||||
+338
-62
@@ -53,23 +53,26 @@ class Transport:
|
||||
Maximum amount of hops that Reticulum will transport a packet.
|
||||
"""
|
||||
|
||||
PATHFINDER_R = 1 # Retransmit retries
|
||||
PATHFINDER_G = 5 # Retry grace period
|
||||
PATHFINDER_RW = 0.5 # Random window for announce rebroadcast
|
||||
PATHFINDER_E = 60*60*24*7 # Path expiration of one week
|
||||
AP_PATH_TIME = 60*60*24 # Path expiration of one day for Access Point paths
|
||||
PATHFINDER_R = 1 # Retransmit retries
|
||||
PATHFINDER_G = 5 # Retry grace period
|
||||
PATHFINDER_RW = 0.5 # Random window for announce rebroadcast
|
||||
PATHFINDER_E = 60*60*24*7 # Path expiration of one week
|
||||
AP_PATH_TIME = 60*60*24 # Path expiration of one day for Access Point paths
|
||||
ROAMING_PATH_TIME = 60*60*6 # Path expiration of 6 hours for Roaming paths
|
||||
|
||||
# TODO: Calculate an optimal number for this in
|
||||
# various situations
|
||||
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
|
||||
|
||||
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_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
|
||||
|
||||
LINK_TIMEOUT = RNS.Link.KEEPALIVE * 2
|
||||
LINK_TIMEOUT = RNS.Link.STALE_TIME * 1.25
|
||||
REVERSE_TIMEOUT = 30*60 # Reverse table entries are removed after max 30 minutes
|
||||
DESTINATION_TIMEOUT = PATHFINDER_E # 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
|
||||
|
||||
interfaces = [] # All active interfaces
|
||||
destinations = [] # All active destinations
|
||||
@@ -89,6 +92,11 @@ class Transport:
|
||||
held_announces = {} # A table containing temporarily held announce-table entries
|
||||
announce_handlers = [] # A table storing externally registered announce handlers
|
||||
tunnels = {} # A table storing tunnels to other transport instances
|
||||
announce_rate_table = {} # A table for keeping track of announce rates
|
||||
|
||||
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
|
||||
max_pr_tags = 32000 # Maximum amount of unique path request tags to remember
|
||||
|
||||
# Transport control destinations are used
|
||||
# for control purposes like path requests
|
||||
@@ -276,6 +284,10 @@ class Transport:
|
||||
def jobs():
|
||||
outgoing = []
|
||||
Transport.jobs_running = True
|
||||
|
||||
# TODO: Remove at some point
|
||||
# start_time = time.time()
|
||||
|
||||
try:
|
||||
if not Transport.jobs_locked:
|
||||
# Process receipts list for timed-out packets
|
||||
@@ -349,10 +361,14 @@ class Transport:
|
||||
Transport.announces_last_checked = time.time()
|
||||
|
||||
|
||||
# Cull the packet hashlist if it has reached max size
|
||||
# Cull the packet hashlist if it has reached its max size
|
||||
if len(Transport.packet_hashlist) > Transport.hashlist_maxsize:
|
||||
Transport.packet_hashlist = Transport.packet_hashlist[len(Transport.packet_hashlist)-Transport.hashlist_maxsize:len(Transport.packet_hashlist)-1]
|
||||
|
||||
# Cull the path request tags list if it has reached its max size
|
||||
if len(Transport.discovery_pr_tags) > Transport.max_pr_tags:
|
||||
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:
|
||||
# Cull the reverse table according to timeout
|
||||
stale_reverse_entries = []
|
||||
@@ -374,13 +390,29 @@ class Transport:
|
||||
destination_entry = Transport.destination_table[destination_hash]
|
||||
attached_interface = destination_entry[5]
|
||||
|
||||
if time.time() > destination_entry[0] + Transport.DESTINATION_TIMEOUT:
|
||||
if attached_interface != None and hasattr(attached_interface, "mode") and attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
destination_expiry = destination_entry[0] + Transport.AP_PATH_TIME
|
||||
elif attached_interface != None and hasattr(attached_interface, "mode") and attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
destination_expiry = destination_entry[0] + Transport.ROAMING_PATH_TIME
|
||||
else:
|
||||
destination_expiry = destination_entry[0] + Transport.DESTINATION_TIMEOUT
|
||||
|
||||
if time.time() > destination_expiry:
|
||||
stale_paths.append(destination_hash)
|
||||
RNS.log("Path to "+RNS.prettyhexrep(destination_hash)+" timed out and was removed", RNS.LOG_DEBUG)
|
||||
elif not attached_interface in Transport.interfaces:
|
||||
stale_paths.append(destination_hash)
|
||||
RNS.log("Path to "+RNS.prettyhexrep(destination_hash)+" was removed since the attached interface no longer exists", RNS.LOG_DEBUG)
|
||||
|
||||
# Cull the pending discovery path requests table
|
||||
stale_discovery_path_requests = []
|
||||
for destination_hash in Transport.discovery_path_requests:
|
||||
entry = Transport.discovery_path_requests[destination_hash]
|
||||
|
||||
if time.time() > entry["timeout"]:
|
||||
stale_discovery_path_requests.append(destination_hash)
|
||||
RNS.log("Waiting path request for "+RNS.prettyhexrep(destination_hash)+" timed out and was removed", RNS.LOG_DEBUG)
|
||||
|
||||
# Cull the tunnel table
|
||||
stale_tunnels = []
|
||||
ti = 0
|
||||
@@ -449,6 +481,17 @@ class Transport:
|
||||
else:
|
||||
RNS.log("Removed "+str(i)+" paths", RNS.LOG_EXTREME)
|
||||
|
||||
i = 0
|
||||
for destination_hash in stale_discovery_path_requests:
|
||||
Transport.discovery_path_requests.pop(destination_hash)
|
||||
i += 1
|
||||
|
||||
if i > 0:
|
||||
if i == 1:
|
||||
RNS.log("Removed "+str(i)+" waiting path request", RNS.LOG_EXTREME)
|
||||
else:
|
||||
RNS.log("Removed "+str(i)+" waiting path requests", RNS.LOG_EXTREME)
|
||||
|
||||
i = 0
|
||||
for tunnel_id in stale_tunnels:
|
||||
Transport.tunnels.pop(tunnel_id)
|
||||
@@ -469,6 +512,13 @@ class Transport:
|
||||
|
||||
Transport.jobs_running = False
|
||||
|
||||
# TODO: Remove at some point
|
||||
# end_time = time.time()
|
||||
# if RNS.loglevel >= RNS.LOG_EXTREME:
|
||||
# duration = round((end_time - start_time) * 1000, 2)
|
||||
# if duration > 1:
|
||||
# RNS.log("Transport jobs took "+str(duration)+"ms", RNS.LOG_EXTREME)
|
||||
|
||||
for packet in outgoing:
|
||||
packet.send()
|
||||
|
||||
@@ -499,8 +549,10 @@ class Transport:
|
||||
sleep(0.01)
|
||||
|
||||
Transport.jobs_locked = True
|
||||
|
||||
# TODO: This updateHash call might be redundant
|
||||
packet.update_hash()
|
||||
|
||||
sent = False
|
||||
outbound_time = time.time()
|
||||
|
||||
@@ -575,7 +627,30 @@ class Transport:
|
||||
if interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to AP mode", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
|
||||
|
||||
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
from_interface = Transport.next_hop_interface(packet.destination_hash)
|
||||
if from_interface == None or not hasattr(from_interface, "mode"):
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface is non-existing or has no mode configured", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
else:
|
||||
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
elif from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to boundary-mode next-hop interface", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
|
||||
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||
from_interface = Transport.next_hop_interface(packet.destination_hash)
|
||||
if from_interface == None or not hasattr(from_interface, "mode"):
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface is non-existing or has no mode configured", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
else:
|
||||
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", RNS.LOG_EXTREME)
|
||||
should_transmit = False
|
||||
|
||||
else:
|
||||
# Currently, annouces originating locally are always
|
||||
# allowed, and do not conform to bandwidth caps.
|
||||
@@ -592,7 +667,6 @@ 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:
|
||||
tx_time = (len(packet.raw)*8) / interface.bitrate
|
||||
wait_time = (tx_time / interface.announce_cap)
|
||||
@@ -605,24 +679,50 @@ class Transport:
|
||||
else:
|
||||
should_transmit = False
|
||||
if not len(interface.announce_queue) >= RNS.Reticulum.MAX_QUEUED_ANNOUNCES:
|
||||
entry = {"time": outbound_time, "hops": packet.hops, "raw": packet.raw}
|
||||
queued_announces = True if len(interface.announce_queue) > 0 else False
|
||||
interface.announce_queue.append(entry)
|
||||
should_queue = True
|
||||
|
||||
if not queued_announces:
|
||||
wait_time = max(interface.announce_allowed_at - time.time(), 0)
|
||||
timer = threading.Timer(wait_time, interface.process_announce_queue)
|
||||
timer.start()
|
||||
already_queued = False
|
||||
for e in interface.announce_queue:
|
||||
if e["destination"] == packet.destination_hash:
|
||||
already_queued = True
|
||||
existing_entry = e
|
||||
|
||||
wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
ql_str = str(len(interface.announce_queue))
|
||||
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
emission_timestamp = Transport.announce_emitted(packet)
|
||||
if already_queued:
|
||||
should_queue = False
|
||||
|
||||
else:
|
||||
wait_time = max(interface.announce_allowed_at - time.time(), 0)
|
||||
wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
ql_str = str(len(interface.announce_queue))
|
||||
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
if emission_timestamp > existing_entry["emitted"]:
|
||||
e["time"] = outbound_time
|
||||
e["hops"] = packet.hops
|
||||
e["emitted"] = emission_timestamp
|
||||
e["raw"] = packet.raw
|
||||
|
||||
if should_queue:
|
||||
entry = {
|
||||
"destination": packet.destination_hash,
|
||||
"time": outbound_time,
|
||||
"hops": packet.hops,
|
||||
"emitted": Transport.announce_emitted(packet),
|
||||
"raw": packet.raw
|
||||
}
|
||||
|
||||
queued_announces = True if len(interface.announce_queue) > 0 else False
|
||||
interface.announce_queue.append(entry)
|
||||
|
||||
if not queued_announces:
|
||||
wait_time = max(interface.announce_allowed_at - time.time(), 0)
|
||||
timer = threading.Timer(wait_time, interface.process_announce_queue)
|
||||
timer.start()
|
||||
|
||||
wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
ql_str = str(len(interface.announce_queue))
|
||||
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
|
||||
else:
|
||||
wait_time = max(interface.announce_allowed_at - time.time(), 0)
|
||||
wait_time_str = str(round(wait_time*1000,3))+"ms"
|
||||
ql_str = str(len(interface.announce_queue))
|
||||
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME)
|
||||
|
||||
else:
|
||||
pass
|
||||
@@ -985,8 +1085,9 @@ class Transport:
|
||||
# First, check that the announce is not for a destination
|
||||
# local to this system, and that hops are less than the max
|
||||
if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1):
|
||||
announce_emitted = Transport.announce_emitted(packet)
|
||||
|
||||
random_blob = packet.data[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
||||
announce_emitted = int.from_bytes(random_blob[5:10], "big")
|
||||
random_blobs = []
|
||||
if packet.destination_hash in Transport.destination_table:
|
||||
random_blobs = Transport.destination_table[packet.destination_hash][4]
|
||||
@@ -1044,6 +1145,42 @@ class Transport:
|
||||
|
||||
if should_add:
|
||||
now = time.time()
|
||||
|
||||
rate_blocked = False
|
||||
if packet.context != RNS.Packet.PATH_RESPONSE and packet.receiving_interface.announce_rate_target != None:
|
||||
if not packet.destination_hash in Transport.announce_rate_table:
|
||||
rate_entry = { "last": now, "rate_violations": 0, "blocked_until": 0, "timestamps": [now]}
|
||||
Transport.announce_rate_table[packet.destination_hash] = rate_entry
|
||||
|
||||
else:
|
||||
rate_entry = Transport.announce_rate_table[packet.destination_hash]
|
||||
rate_entry["timestamps"].append(now)
|
||||
|
||||
while len(rate_entry["timestamps"]) > Transport.MAX_RATE_TIMESTAMPS:
|
||||
rate_entry["timestamps"].pop(0)
|
||||
|
||||
current_rate = now - rate_entry["last"]
|
||||
|
||||
if now > rate_entry["blocked_until"]:
|
||||
|
||||
if current_rate < packet.receiving_interface.announce_rate_target:
|
||||
rate_entry["rate_violations"] += 1
|
||||
|
||||
else:
|
||||
rate_entry["rate_violations"] = max(0, rate_entry["rate_violations"]-1)
|
||||
|
||||
if rate_entry["rate_violations"] > packet.receiving_interface.announce_rate_grace:
|
||||
rate_target = packet.receiving_interface.announce_rate_target
|
||||
rate_penalty = packet.receiving_interface.announce_rate_penalty
|
||||
rate_entry["blocked_until"] = rate_entry["last"] + rate_target + rate_penalty
|
||||
rate_blocked = True
|
||||
else:
|
||||
rate_entry["last"] = now
|
||||
|
||||
else:
|
||||
rate_blocked = True
|
||||
|
||||
|
||||
retries = 0
|
||||
announce_hops = packet.hops
|
||||
local_rebroadcasts = 0
|
||||
@@ -1054,6 +1191,8 @@ class Transport:
|
||||
|
||||
if packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
expires = now + Transport.AP_PATH_TIME
|
||||
elif packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
expires = now + Transport.ROAMING_PATH_TIME
|
||||
else:
|
||||
expires = now + Transport.PATHFINDER_E
|
||||
|
||||
@@ -1062,23 +1201,27 @@ class Transport:
|
||||
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
|
||||
|
||||
if Transport.from_local_client(packet):
|
||||
# If the announce is from a local client,
|
||||
# it is announced immediately, but only one time.
|
||||
retransmit_timeout = now
|
||||
retries = Transport.PATHFINDER_R
|
||||
if rate_blocked:
|
||||
RNS.log("Blocking rebroadcast of announce from "+RNS.prettyhexrep(packet.destination_hash)+" due to excessive announce rate", RNS.LOG_DEBUG)
|
||||
|
||||
else:
|
||||
if Transport.from_local_client(packet):
|
||||
# If the announce is from a local client,
|
||||
# it is announced immediately, but only one time.
|
||||
retransmit_timeout = now
|
||||
retries = Transport.PATHFINDER_R
|
||||
|
||||
Transport.announce_table[packet.destination_hash] = [
|
||||
now,
|
||||
retransmit_timeout,
|
||||
retries,
|
||||
received_from,
|
||||
announce_hops,
|
||||
packet,
|
||||
local_rebroadcasts,
|
||||
block_rebroadcasts,
|
||||
attached_interface
|
||||
]
|
||||
Transport.announce_table[packet.destination_hash] = [
|
||||
now,
|
||||
retransmit_timeout,
|
||||
retries,
|
||||
received_from,
|
||||
announce_hops,
|
||||
packet,
|
||||
local_rebroadcasts,
|
||||
block_rebroadcasts,
|
||||
attached_interface
|
||||
]
|
||||
|
||||
# TODO: Check from_local_client once and store result
|
||||
elif Transport.from_local_client(packet) and packet.context == RNS.Packet.PATH_RESPONSE:
|
||||
@@ -1113,6 +1256,7 @@ class Transport:
|
||||
announce_context = RNS.Packet.NONE
|
||||
announce_data = packet.data
|
||||
|
||||
# TODO: Shouldn't the context be PATH_RESPONSE in the first case here?
|
||||
if Transport.from_local_client(packet) and packet.context == RNS.Packet.PATH_RESPONSE:
|
||||
for local_interface in Transport.local_client_interfaces:
|
||||
if packet.receiving_interface != local_interface:
|
||||
@@ -1147,6 +1291,38 @@ class Transport:
|
||||
new_announce.hops = packet.hops
|
||||
new_announce.send()
|
||||
|
||||
# If we have any waiting discovery path requests
|
||||
# for this destination, we retransmit to that
|
||||
# interface immediately
|
||||
if packet.destination_hash in Transport.discovery_path_requests:
|
||||
pr_entry = Transport.discovery_path_requests[packet.destination_hash]
|
||||
attached_interface = pr_entry["requesting_interface"]
|
||||
|
||||
interface_str = " on "+str(attached_interface)
|
||||
|
||||
RNS.log("Got matching announce, answering waiting discovery path request for "+RNS.prettyhexrep(packet.destination_hash)+interface_str, RNS.LOG_DEBUG)
|
||||
announce_identity = RNS.Identity.recall(packet.destination_hash)
|
||||
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
|
||||
announce_destination.hash = packet.destination_hash
|
||||
announce_destination.hexhash = announce_destination.hash.hex()
|
||||
announce_context = RNS.Packet.NONE
|
||||
announce_data = packet.data
|
||||
|
||||
new_announce = RNS.Packet(
|
||||
announce_destination,
|
||||
announce_data,
|
||||
RNS.Packet.ANNOUNCE,
|
||||
context = RNS.Packet.PATH_RESPONSE,
|
||||
header_type = RNS.Packet.HEADER_2,
|
||||
transport_type = Transport.TRANSPORT,
|
||||
transport_id = Transport.identity.hash,
|
||||
attached_interface = attached_interface
|
||||
)
|
||||
|
||||
new_announce.hops = packet.hops
|
||||
new_announce.send()
|
||||
|
||||
|
||||
destination_table_entry = [now, received_from, announce_hops, expires, random_blobs, packet.receiving_interface, packet]
|
||||
Transport.destination_table[packet.destination_hash] = destination_table_entry
|
||||
RNS.log("Destination "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
|
||||
@@ -1229,7 +1405,7 @@ class Transport:
|
||||
if packet.receiving_interface == link_entry[2]:
|
||||
# TODO: Should we validate the LR proof at each transport
|
||||
# step before transporting it?
|
||||
RNS.log("Link request proof received on correct interface, transporting it via "+str(link_entry[4]), RNS.LOG_DEBUG)
|
||||
# RNS.log("Link request proof received on correct interface, transporting it 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:]
|
||||
@@ -1591,7 +1767,7 @@ class Transport:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def request_path(destination_hash, on_interface=None):
|
||||
def request_path(destination_hash, on_interface=None, tag=None, recursive=False):
|
||||
"""
|
||||
Requests a path to the destination from the network. If
|
||||
another reachable peer on the network knows a path, it
|
||||
@@ -1600,13 +1776,45 @@ class Transport:
|
||||
:param destination_hash: A destination hash as *bytes*.
|
||||
:param on_interface: If specified, the path request will only be sent on this interface. In normal use, Reticulum handles this automatically, and this parameter should not be used.
|
||||
"""
|
||||
if RNS.Reticulum.transport_enabled():
|
||||
path_request_data = destination_hash+Transport.identity.hash+RNS.Identity.get_random_hash()
|
||||
if tag == None:
|
||||
request_tag = RNS.Identity.get_random_hash()
|
||||
else:
|
||||
path_request_data = destination_hash+RNS.Identity.get_random_hash()
|
||||
request_tag = tag
|
||||
|
||||
if RNS.Reticulum.transport_enabled():
|
||||
path_request_data = destination_hash+Transport.identity.hash+request_tag
|
||||
else:
|
||||
path_request_data = destination_hash+request_tag
|
||||
|
||||
path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||
packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1, attached_interface = on_interface)
|
||||
|
||||
if on_interface != None and recursive:
|
||||
if not hasattr(on_interface, "announce_cap"):
|
||||
on_interface.announce_cap = RNS.Reticulum.ANNOUNCE_CAP
|
||||
|
||||
if not hasattr(on_interface, "announce_allowed_at"):
|
||||
on_interface.announce_allowed_at = 0
|
||||
|
||||
if not hasattr(on_interface, "announce_queue"):
|
||||
on_interface.announce_queue = []
|
||||
|
||||
queued_announces = True if len(on_interface.announce_queue) > 0 else False
|
||||
if queued_announces:
|
||||
# TODO: Reset to extra level, probably
|
||||
RNS.log("Blocking recursive path request on "+str(on_interface)+" due to queued announces", RNS.LOG_DEBUG)
|
||||
return
|
||||
else:
|
||||
now = time.time()
|
||||
if now < on_interface.announce_allowed_at:
|
||||
# TODO: Reset to extra level, probably
|
||||
RNS.log("Blocking recursive path request on "+str(on_interface)+" due to active announce cap", RNS.LOG_DEBUG)
|
||||
return
|
||||
else:
|
||||
tx_time = ((len(path_request_data)+RNS.Reticulum.HEADER_MINSIZE)*8) / on_interface.bitrate
|
||||
wait_time = (tx_time / on_interface.announce_cap)
|
||||
on_interface.announce_allowed_at = now + wait_time
|
||||
|
||||
packet.send()
|
||||
|
||||
@staticmethod
|
||||
@@ -1616,8 +1824,9 @@ class Transport:
|
||||
# hash in the packet, we assume those bytes are the
|
||||
# destination being requested.
|
||||
if len(data) >= RNS.Identity.TRUNCATED_HASHLENGTH//8:
|
||||
# If there is also enough bytes for a trasport
|
||||
# instance ID and at least one random byte, we
|
||||
destination_hash = data[:RNS.Identity.TRUNCATED_HASHLENGTH//8]
|
||||
# If there is also enough bytes for a transport
|
||||
# instance ID and at least one tag byte, we
|
||||
# assume the next bytes to be the trasport ID
|
||||
# of the requesting transport instance.
|
||||
if len(data) > (RNS.Identity.TRUNCATED_HASHLENGTH//8)*2:
|
||||
@@ -1625,24 +1834,54 @@ class Transport:
|
||||
else:
|
||||
requesting_transport_instance = None
|
||||
|
||||
Transport.path_request(
|
||||
data[:RNS.Identity.TRUNCATED_HASHLENGTH//8],
|
||||
Transport.from_local_client(packet),
|
||||
packet.receiving_interface,
|
||||
requesting_transport_instance,
|
||||
)
|
||||
tag_bytes = None
|
||||
if len(data) > (RNS.Identity.TRUNCATED_HASHLENGTH//8)*2:
|
||||
tag_bytes = data[RNS.Identity.TRUNCATED_HASHLENGTH//8*2:]
|
||||
|
||||
elif len(data) > (RNS.Identity.TRUNCATED_HASHLENGTH//8):
|
||||
tag_bytes = data[RNS.Identity.TRUNCATED_HASHLENGTH//8:]
|
||||
|
||||
if tag_bytes != None:
|
||||
if len(tag_bytes) > RNS.Identity.TRUNCATED_HASHLENGTH//8:
|
||||
tag_bytes = tag_bytes[:RNS.Identity.TRUNCATED_HASHLENGTH//8]
|
||||
|
||||
unique_tag = destination_hash+tag_bytes
|
||||
|
||||
if not unique_tag in Transport.discovery_pr_tags:
|
||||
Transport.discovery_pr_tags.append(unique_tag)
|
||||
|
||||
Transport.path_request(
|
||||
destination_hash,
|
||||
Transport.from_local_client(packet),
|
||||
packet.receiving_interface,
|
||||
requestor_transport_id = requesting_transport_instance,
|
||||
tag=tag_bytes
|
||||
)
|
||||
|
||||
else:
|
||||
# TODO: Reset this to debug level
|
||||
RNS.log("Ignoring duplicate path request for "+RNS.prettyhexrep(destination_hash)+" with tag "+RNS.prettyhexrep(unique_tag), RNS.LOG_WARNING)
|
||||
|
||||
else:
|
||||
# TODO: Reset this to debug level
|
||||
RNS.log("Ignoring tagless path request for "+RNS.prettyhexrep(destination_hash), RNS.LOG_WARNING)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while handling path request. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
@staticmethod
|
||||
def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None):
|
||||
def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None, tag=None):
|
||||
should_search_for_unknown = False
|
||||
|
||||
if attached_interface != None:
|
||||
if RNS.Reticulum.transport_enabled() and attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR:
|
||||
should_search_for_unknown = True
|
||||
|
||||
interface_str = " on "+str(attached_interface)
|
||||
else:
|
||||
interface_str = ""
|
||||
|
||||
# TODO: Clean
|
||||
# RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG)
|
||||
RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG)
|
||||
|
||||
destination_exists_on_local_client = False
|
||||
if len(Transport.local_client_interfaces) > 0:
|
||||
@@ -1670,7 +1909,7 @@ class Transport:
|
||||
# path requests with transport instance keys is quite
|
||||
# inefficient. There is probably a better way. Doing
|
||||
# path invalidation here would decrease the network
|
||||
# convergence time.
|
||||
# convergence time. Maybe just drop it?
|
||||
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG)
|
||||
@@ -1707,12 +1946,28 @@ class Transport:
|
||||
if not interface == attached_interface:
|
||||
Transport.request_path(destination_hash, interface)
|
||||
|
||||
elif should_search_for_unknown:
|
||||
if destination_hash in Transport.discovery_path_requests:
|
||||
RNS.log("There is already a waiting path request for "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG)
|
||||
else:
|
||||
# Forward path request on all interfaces
|
||||
# except the requestor interface
|
||||
RNS.log("Attempting to discover unknown path to "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG)
|
||||
pr_entry = { "destination_hash": destination_hash, "timeout": time.time()+Transport.PATH_REQUEST_TIMEOUT, "requesting_interface": attached_interface }
|
||||
Transport.discovery_path_requests[destination_hash] = pr_entry
|
||||
|
||||
for interface in Transport.interfaces:
|
||||
if not interface == attached_interface:
|
||||
# Use the previously extracted tag from this path request
|
||||
# on the new path requests as well, to avoid potential loops
|
||||
Transport.request_path(destination_hash, on_interface=interface, tag=tag, recursive=True)
|
||||
|
||||
elif not is_from_local_client and len(Transport.local_client_interfaces) > 0:
|
||||
# Forward the path request on all local
|
||||
# client interfaces
|
||||
RNS.log("Forwarding path request for "+RNS.prettyhexrep(destination_hash)+interface_str+" to local clients", RNS.LOG_DEBUG)
|
||||
for interface in Transport.local_client_interfaces:
|
||||
Transport.request_path(destination_hash, interface)
|
||||
Transport.request_path(destination_hash, on_interface=interface)
|
||||
|
||||
else:
|
||||
RNS.log("Ignoring path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", no path known", RNS.LOG_DEBUG)
|
||||
@@ -1772,6 +2027,27 @@ class Transport:
|
||||
if registered_destination.type == RNS.Destination.SINGLE:
|
||||
registered_destination.announce(path_response=True)
|
||||
|
||||
@staticmethod
|
||||
def drop_announce_queues():
|
||||
for interface in Transport.interfaces:
|
||||
if hasattr(interface, "announce_queue") and interface.announce_queue != None:
|
||||
na = len(interface.announce_queue)
|
||||
if na > 0:
|
||||
if na == 1:
|
||||
na_str = "1 announce"
|
||||
else:
|
||||
na_str = str(na)+" announces"
|
||||
|
||||
interface.announce_queue = []
|
||||
RNS.log("Dropped "+na_str+" on "+str(interface), RNS.LOG_VERBOSE)
|
||||
|
||||
@staticmethod
|
||||
def announce_emitted(packet):
|
||||
random_blob = packet.data[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
||||
announce_emitted = int.from_bytes(random_blob[5:10], "big")
|
||||
|
||||
return announce_emitted
|
||||
|
||||
@staticmethod
|
||||
def exit_handler():
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 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
|
||||
import sys
|
||||
import os
|
||||
|
||||
from RNS._version import __version__
|
||||
|
||||
APP_NAME = "rncp"
|
||||
allow_all = False
|
||||
allowed_identity_hashes = []
|
||||
|
||||
def receive(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False, limit = None, disable_auth = None,disable_announce=False):
|
||||
global allow_all, allowed_identity_hashes
|
||||
identity = None
|
||||
|
||||
targetloglevel = 3+verbosity-quietness
|
||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||
|
||||
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
||||
if os.path.isfile(identity_path):
|
||||
identity = RNS.Identity.from_file(identity_path)
|
||||
|
||||
if identity == None:
|
||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||
identity = RNS.Identity()
|
||||
identity.to_file(identity_path)
|
||||
|
||||
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "receive")
|
||||
|
||||
if display_identity:
|
||||
print("Identity : "+str(identity))
|
||||
print("Receiving on : "+RNS.prettyhexrep(destination.hash))
|
||||
exit(0)
|
||||
|
||||
if disable_auth:
|
||||
allow_all = True
|
||||
else:
|
||||
if allowed != None:
|
||||
for a in allowed:
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(a) != dest_len:
|
||||
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(a)
|
||||
allowed_identity_hashes.append(destination_hash)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit(1)
|
||||
|
||||
if len(allowed_identity_hashes) < 1 and not disable_auth:
|
||||
print("Warning: No allowed identities configured, rncp will not accept any files!")
|
||||
|
||||
destination.set_link_established_callback(receive_link_established)
|
||||
print("rncp ready to receive on "+RNS.prettyhexrep(destination.hash))
|
||||
|
||||
if not disable_announce:
|
||||
destination.announce()
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
def receive_link_established(link):
|
||||
RNS.log("Incoming link established", RNS.LOG_VERBOSE)
|
||||
link.set_remote_identified_callback(receive_sender_identified)
|
||||
link.set_resource_strategy(RNS.Link.ACCEPT_APP)
|
||||
link.set_resource_callback(receive_resource_callback)
|
||||
link.set_resource_started_callback(receive_resource_started)
|
||||
link.set_resource_concluded_callback(receive_resource_concluded)
|
||||
|
||||
def receive_sender_identified(link, identity):
|
||||
if identity.hash in allowed_identity_hashes:
|
||||
RNS.log("Authenticated sender", RNS.LOG_VERBOSE)
|
||||
else:
|
||||
RNS.log("Sender not allowed, tearing down link", RNS.LOG_VERBOSE)
|
||||
link.teardown()
|
||||
|
||||
def receive_resource_callback(resource):
|
||||
sender_identity = resource.link.get_remote_identity()
|
||||
|
||||
if sender_identity != None:
|
||||
if sender_identity.hash in allowed_identity_hashes:
|
||||
print("Allowing sender")
|
||||
return True
|
||||
|
||||
print("Rejecting sender")
|
||||
return False
|
||||
|
||||
def receive_resource_started(resource):
|
||||
if resource.link.get_remote_identity():
|
||||
id_str = " from "+RNS.prettyhexrep(resource.link.get_remote_identity().hash)
|
||||
else:
|
||||
id_str = ""
|
||||
|
||||
print("Starting resource transfer "+RNS.prettyhexrep(resource.hash)+id_str)
|
||||
|
||||
def receive_resource_concluded(resource):
|
||||
if resource.status == RNS.Resource.COMPLETE:
|
||||
print(str(resource)+" completed")
|
||||
|
||||
if resource.total_size > 4:
|
||||
filename_len = int.from_bytes(resource.data.read(2), "big")
|
||||
filename = resource.data.read(filename_len).decode("utf-8")
|
||||
|
||||
counter = 0
|
||||
saved_filename = filename
|
||||
while os.path.isfile(saved_filename):
|
||||
counter += 1
|
||||
saved_filename = filename+"."+str(counter)
|
||||
|
||||
file = open(saved_filename, "wb")
|
||||
file.write(resource.data.read())
|
||||
file.close()
|
||||
|
||||
else:
|
||||
print("Invalid data received, ignoring resource")
|
||||
|
||||
else:
|
||||
print("Resource failed")
|
||||
|
||||
resource_done = False
|
||||
current_resource = None
|
||||
stats = []
|
||||
speed = 0.0
|
||||
def sender_progress(resource):
|
||||
stats_max = 32
|
||||
global current_resource, stats, speed, resource_done
|
||||
current_resource = resource
|
||||
now = time.time()
|
||||
got = current_resource.get_progress()*current_resource.total_size
|
||||
entry = [now, got]
|
||||
stats.append(entry)
|
||||
while len(stats) > stats_max:
|
||||
stats.pop(0)
|
||||
|
||||
span = now - stats[0][0]
|
||||
if span == 0:
|
||||
speed = 0
|
||||
else:
|
||||
diff = got - stats[0][1]
|
||||
speed = diff/span
|
||||
|
||||
if resource.status < RNS.Resource.COMPLETE:
|
||||
resource_done = False
|
||||
else:
|
||||
resource_done = True
|
||||
|
||||
link = None
|
||||
def send(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT):
|
||||
global current_resource, resource_done, link, speed
|
||||
from tempfile import TemporaryFile
|
||||
targetloglevel = 3+verbosity-quietness
|
||||
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(destination) != dest_len:
|
||||
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(destination)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit(1)
|
||||
|
||||
|
||||
file_path = os.path.expanduser(file)
|
||||
if not os.path.isfile(file_path):
|
||||
print("File not found")
|
||||
exit(1)
|
||||
|
||||
temp_file = TemporaryFile()
|
||||
real_file = open(file_path, "rb")
|
||||
filename_bytes = os.path.basename(file_path).encode("utf-8")
|
||||
filename_len = len(filename_bytes)
|
||||
|
||||
if filename_len > 0xFFFF:
|
||||
print("Filename exceeds max size, cannot send")
|
||||
exit(1)
|
||||
else:
|
||||
print("Preparing file...", end=" ")
|
||||
|
||||
temp_file.write(filename_len.to_bytes(2, "big"))
|
||||
temp_file.write(filename_bytes)
|
||||
temp_file.write(real_file.read())
|
||||
temp_file.seek(0)
|
||||
|
||||
print("\r \r", end="")
|
||||
|
||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||
|
||||
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
||||
if os.path.isfile(identity_path):
|
||||
identity = RNS.Identity.from_file(identity_path)
|
||||
|
||||
if identity == None:
|
||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||
identity = RNS.Identity()
|
||||
identity.to_file(identity_path)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
RNS.Transport.request_path(destination_hash)
|
||||
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
|
||||
sys.stdout.flush()
|
||||
|
||||
i = 0
|
||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||
estab_timeout = time.time()+timeout
|
||||
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
print("\r \rPath not found")
|
||||
exit(1)
|
||||
else:
|
||||
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ")
|
||||
|
||||
receiver_identity = RNS.Identity.recall(destination_hash)
|
||||
receiver_destination = RNS.Destination(
|
||||
receiver_identity,
|
||||
RNS.Destination.OUT,
|
||||
RNS.Destination.SINGLE,
|
||||
APP_NAME,
|
||||
"receive"
|
||||
)
|
||||
|
||||
link = RNS.Link(receiver_destination)
|
||||
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
print("\r \rCould not establish link with "+RNS.prettyhexrep(destination_hash))
|
||||
exit(1)
|
||||
else:
|
||||
print("\r \rAdvertising file resource ", end=" ")
|
||||
|
||||
link.identify(identity)
|
||||
resource = RNS.Resource(temp_file, link, callback = sender_progress, progress_callback = sender_progress)
|
||||
current_resource = resource
|
||||
|
||||
while resource.status < RNS.Resource.TRANSFERRING:
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
|
||||
if resource.status > RNS.Resource.COMPLETE:
|
||||
print("\r \rFile was not accepted by "+RNS.prettyhexrep(destination_hash))
|
||||
exit(1)
|
||||
else:
|
||||
print("\r \rTransferring file ", end=" ")
|
||||
|
||||
while not resource_done:
|
||||
time.sleep(0.1)
|
||||
prg = current_resource.get_progress()
|
||||
percent = round(prg * 100.0, 1)
|
||||
stat_str = str(percent)+"% - " + size_str(int(prg*current_resource.total_size)) + " of " + size_str(current_resource.total_size) + " - " +size_str(speed, "b")+"ps"
|
||||
print("\r \rTransferring file "+syms[i]+" "+stat_str, end=" ")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
if current_resource.status != RNS.Resource.COMPLETE:
|
||||
print("\r \rThe transfer failed")
|
||||
exit(1)
|
||||
else:
|
||||
print("\r \r"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
|
||||
link.teardown()
|
||||
time.sleep(0.25)
|
||||
real_file.close()
|
||||
temp_file.close()
|
||||
exit(0)
|
||||
|
||||
def main():
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description="Reticulum File Transfer Utility")
|
||||
parser.add_argument("file", nargs="?", default=None, help="file to be transferred", type=str)
|
||||
parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the receiver", type=str)
|
||||
parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0, help="increase verbosity")
|
||||
parser.add_argument('-q', '--quiet', action='count', default=0, help="decrease verbosity")
|
||||
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
|
||||
parser.add_argument("-r", '--receive', action='store_true', default=False, help="wait for incoming files")
|
||||
parser.add_argument("-b", '--no-announce', action='store_true', default=False, help="don't announce at program start")
|
||||
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="accept from this identity", type=str)
|
||||
parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept files from anyone")
|
||||
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
||||
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
|
||||
parser.add_argument("--version", action="version", version="rncp {version}".format(version=__version__))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.receive or args.print_identity:
|
||||
receive(
|
||||
configdir = args.config,
|
||||
verbosity=args.verbose,
|
||||
quietness=args.quiet,
|
||||
allowed = args.allowed,
|
||||
display_identity=args.print_identity,
|
||||
# limit=args.limit,
|
||||
disable_auth=args.no_auth,
|
||||
disable_announce=args.no_announce,
|
||||
)
|
||||
|
||||
elif args.destination != None and args.file != None:
|
||||
send(
|
||||
configdir = args.config,
|
||||
verbosity = args.verbose,
|
||||
quietness = args.quiet,
|
||||
destination = args.destination,
|
||||
file = args.file,
|
||||
timeout = args.w,
|
||||
)
|
||||
|
||||
else:
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("")
|
||||
if resource != None:
|
||||
resource.cancel()
|
||||
if link != None:
|
||||
link.teardown()
|
||||
exit()
|
||||
|
||||
def size_str(num, suffix='B'):
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
|
||||
if suffix == 'b':
|
||||
num *= 8
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
|
||||
for unit in units:
|
||||
if abs(num) < 1000.0:
|
||||
if unit == "":
|
||||
return "%.0f %s%s" % (num, unit, suffix)
|
||||
else:
|
||||
return "%.2f %s%s" % (num, unit, suffix)
|
||||
num /= 1000.0
|
||||
|
||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+161
-11
@@ -30,19 +30,108 @@ import argparse
|
||||
from RNS._version import __version__
|
||||
|
||||
|
||||
def program_setup(configdir, table, drop, destination_hexhash, verbosity, timeout):
|
||||
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues):
|
||||
if table:
|
||||
destination_hash = None
|
||||
if destination_hexhash != None:
|
||||
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)
|
||||
table = sorted(reticulum.get_path_table(), key=lambda e: (e["interface"], e["hops"]) )
|
||||
|
||||
displayed = 0
|
||||
for path in table:
|
||||
exp_str = RNS.timestamp_str(path["expires"])
|
||||
if path["hops"] == 1:
|
||||
m_str = " "
|
||||
else:
|
||||
m_str = "s"
|
||||
print(RNS.prettyhexrep(path["hash"])+" is "+str(path["hops"])+" hop"+m_str+" away via "+RNS.prettyhexrep(path["via"])+" on "+path["interface"]+" expires "+RNS.timestamp_str(path["expires"]))
|
||||
if destination_hash == None or destination_hash == path["hash"]:
|
||||
displayed += 1
|
||||
exp_str = RNS.timestamp_str(path["expires"])
|
||||
if path["hops"] == 1:
|
||||
m_str = " "
|
||||
else:
|
||||
m_str = "s"
|
||||
print(RNS.prettyhexrep(path["hash"])+" is "+str(path["hops"])+" hop"+m_str+" away via "+RNS.prettyhexrep(path["via"])+" on "+path["interface"]+" expires "+RNS.timestamp_str(path["expires"]))
|
||||
|
||||
if destination_hash != None and displayed == 0:
|
||||
print("No path known")
|
||||
sys.exit(1)
|
||||
|
||||
elif rates:
|
||||
destination_hash = None
|
||||
if destination_hexhash != None:
|
||||
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)
|
||||
table = sorted(reticulum.get_rate_table(), key=lambda e: e["last"] )
|
||||
|
||||
if len(table) == 0:
|
||||
print("No information available")
|
||||
|
||||
else:
|
||||
displayed = 0
|
||||
for entry in table:
|
||||
if destination_hash == None or destination_hash == entry["hash"]:
|
||||
displayed += 1
|
||||
try:
|
||||
last_str = pretty_date(int(entry["last"]))
|
||||
start_ts = entry["timestamps"][0]
|
||||
span = max(time.time() - start_ts, 3600.0)
|
||||
span_hours = span/3600.0
|
||||
span_str = pretty_date(int(entry["timestamps"][0]))
|
||||
hour_rate = round(len(entry["timestamps"])/span_hours, 3)
|
||||
if hour_rate-int(hour_rate) == 0:
|
||||
hour_rate = int(hour_rate)
|
||||
|
||||
if entry["rate_violations"] > 0:
|
||||
if entry["rate_violations"] == 1:
|
||||
s_str = ""
|
||||
else:
|
||||
s_str = "s"
|
||||
|
||||
rv_str = ", "+str(entry["rate_violations"])+" active rate violation"+s_str
|
||||
else:
|
||||
rv_str = ""
|
||||
|
||||
if entry["blocked_until"] > time.time():
|
||||
bli = time.time()-(int(entry["blocked_until"])-time.time())
|
||||
bl_str = ", new announces allowed in "+pretty_date(int(bli))
|
||||
else:
|
||||
bl_str = ""
|
||||
|
||||
|
||||
print(RNS.prettyhexrep(entry["hash"])+" last heard "+last_str+" ago, "+str(hour_rate)+" announces/hour in the last "+span_str+rv_str+bl_str)
|
||||
|
||||
except Exception as e:
|
||||
print("Error while processing entry for "+RNS.prettyhexrep(entry["hash"]))
|
||||
print(str(e))
|
||||
|
||||
if destination_hash != None and displayed == 0:
|
||||
print("No information available")
|
||||
sys.exit(1)
|
||||
|
||||
elif drop_queues:
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
RNS.log("Dropping announce queues on all interfaces...")
|
||||
reticulum.drop_announce_queues()
|
||||
|
||||
elif drop:
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
@@ -54,7 +143,8 @@ def program_setup(configdir, table, drop, destination_hexhash, verbosity, timeou
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
@@ -62,6 +152,8 @@ def program_setup(configdir, table, drop, destination_hexhash, verbosity, timeou
|
||||
print("Dropped path to "+RNS.prettyhexrep(destination_hash))
|
||||
else:
|
||||
print("Unable to drop path to "+RNS.prettyhexrep(destination_hash)+". Does it exist?")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
else:
|
||||
try:
|
||||
@@ -74,7 +166,8 @@ def program_setup(configdir, table, drop, destination_hexhash, verbosity, timeou
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||
|
||||
@@ -105,6 +198,8 @@ def program_setup(configdir, table, drop, destination_hexhash, verbosity, timeou
|
||||
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface)
|
||||
else:
|
||||
print("\r \rPath not found")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
@@ -132,6 +227,14 @@ def main():
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--rates",
|
||||
action="store_true",
|
||||
help="show announce rate info",
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--drop",
|
||||
@@ -140,13 +243,21 @@ def main():
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-D",
|
||||
"--drop-announces",
|
||||
action="store_true",
|
||||
help="drop all queued announces",
|
||||
default=False
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
action="store",
|
||||
metavar="seconds",
|
||||
type=float,
|
||||
help="timeout before giving up",
|
||||
default=15
|
||||
default=RNS.Transport.PATH_REQUEST_TIMEOUT
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -166,7 +277,7 @@ def main():
|
||||
else:
|
||||
configarg = None
|
||||
|
||||
if not args.table and not args.destination:
|
||||
if not args.drop_announces and not args.table and not args.rates and not args.destination:
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
@@ -174,15 +285,54 @@ def main():
|
||||
program_setup(
|
||||
configdir = configarg,
|
||||
table = args.table,
|
||||
rates = args.rates,
|
||||
drop = args.drop,
|
||||
destination_hexhash = args.destination,
|
||||
verbosity = args.verbose,
|
||||
timeout = args.w,
|
||||
drop_queues = args.drop_announces,
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("")
|
||||
exit()
|
||||
|
||||
def pretty_date(time=False):
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
if type(time) is int:
|
||||
diff = now - datetime.fromtimestamp(time)
|
||||
elif isinstance(time,datetime):
|
||||
diff = now - time
|
||||
elif not time:
|
||||
diff = now - now
|
||||
second_diff = diff.seconds
|
||||
day_diff = diff.days
|
||||
if day_diff < 0:
|
||||
return ''
|
||||
if day_diff == 0:
|
||||
if second_diff < 10:
|
||||
return str(second_diff) + " seconds"
|
||||
if second_diff < 60:
|
||||
return str(second_diff) + " seconds"
|
||||
if second_diff < 120:
|
||||
return "1 minute"
|
||||
if second_diff < 3600:
|
||||
return str(int(second_diff / 60)) + " minutes"
|
||||
if second_diff < 7200:
|
||||
return "an hour"
|
||||
if second_diff < 86400:
|
||||
return str(int(second_diff / 3600)) + " hours"
|
||||
if day_diff == 1:
|
||||
return "1 day"
|
||||
if day_diff < 7:
|
||||
return str(day_diff) + " days"
|
||||
if day_diff < 31:
|
||||
return str(int(day_diff / 7)) + " weeks"
|
||||
if day_diff < 365:
|
||||
return str(int(day_diff / 30)) + " months"
|
||||
return str(int(day_diff / 365)) + " years"
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -147,7 +147,7 @@ loglevel = 4
|
||||
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
enabled = yes
|
||||
|
||||
|
||||
# The following example enables communication with other
|
||||
@@ -155,7 +155,7 @@ loglevel = 4
|
||||
|
||||
[[UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = False
|
||||
enabled = no
|
||||
listen_ip = 0.0.0.0
|
||||
listen_port = 4242
|
||||
forward_ip = 255.255.255.255
|
||||
@@ -198,7 +198,7 @@ loglevel = 4
|
||||
|
||||
[[TCP Server Interface]]
|
||||
type = TCPServerInterface
|
||||
interface_enabled = False
|
||||
enabled = no
|
||||
|
||||
# This configuration will listen on all IP
|
||||
# interfaces on port 4242
|
||||
@@ -224,7 +224,7 @@ loglevel = 4
|
||||
|
||||
[[TCP Client Interface]]
|
||||
type = TCPClientInterface
|
||||
interface_enabled = False
|
||||
enabled = no
|
||||
target_host = 127.0.0.1
|
||||
target_port = 4242
|
||||
|
||||
@@ -237,9 +237,9 @@ loglevel = 4
|
||||
|
||||
[[I2P]]
|
||||
type = I2PInterface
|
||||
interface_enabled = yes
|
||||
enabled = no
|
||||
connectable = yes
|
||||
peers = 5urvjicpzi7q3ybztsef4i5ow2aq4soktfj7zedz53s47r54jnqq.b32.i2p
|
||||
peers = ykzlw5ujbaqc2xkec4cpvgyxj257wcrmmgkuxqmqcur7cq3w3lha.b32.i2p
|
||||
|
||||
|
||||
# Here's an example of how to add a LoRa interface
|
||||
@@ -249,7 +249,7 @@ loglevel = 4
|
||||
type = RNodeInterface
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
enabled = no
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB0
|
||||
@@ -301,7 +301,7 @@ loglevel = 4
|
||||
type = KISSInterface
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
enabled = no
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB1
|
||||
@@ -372,7 +372,7 @@ loglevel = 4
|
||||
ssid = 0
|
||||
|
||||
# Enable interface if you want use it!
|
||||
interface_enabled = False
|
||||
enabled = no
|
||||
|
||||
# Serial port for the device
|
||||
port = /dev/ttyUSB2
|
||||
|
||||
+76
-52
@@ -59,75 +59,99 @@ def program_setup(configdir, dispall=False, verbosity = 0):
|
||||
for ifstat in stats["interfaces"]:
|
||||
name = ifstat["name"]
|
||||
|
||||
if dispall or not (name.startswith("LocalInterface[") or name.startswith("TCPInterface[Client")):
|
||||
print("")
|
||||
if dispall or not (
|
||||
name.startswith("LocalInterface[") or
|
||||
name.startswith("TCPInterface[Client") or
|
||||
name.startswith("I2PInterfacePeer[Connected peer") or
|
||||
(name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False))
|
||||
):
|
||||
|
||||
if ifstat["status"]:
|
||||
ss = "Up"
|
||||
else:
|
||||
ss = "Down"
|
||||
if not (name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False)):
|
||||
print("")
|
||||
|
||||
if ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
modestr = "Access Point"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_POINT_TO_POINT:
|
||||
modestr = "Point-to-Point"
|
||||
else:
|
||||
modestr = "Full"
|
||||
if ifstat["status"]:
|
||||
ss = "Up"
|
||||
else:
|
||||
ss = "Down"
|
||||
|
||||
if ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||
modestr = "Access Point"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_POINT_TO_POINT:
|
||||
modestr = "Point-to-Point"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||
modestr = "Roaming"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||
modestr = "Boundary"
|
||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_GATEWAY:
|
||||
modestr = "Gateway"
|
||||
else:
|
||||
modestr = "Full"
|
||||
|
||||
|
||||
if ifstat["clients"] != None:
|
||||
clients = ifstat["clients"]
|
||||
if name.startswith("Shared Instance["):
|
||||
cnum = max(clients-1,0)
|
||||
if cnum == 1:
|
||||
spec_str = " program"
|
||||
if ifstat["clients"] != None:
|
||||
clients = ifstat["clients"]
|
||||
if name.startswith("Shared Instance["):
|
||||
cnum = max(clients-1,0)
|
||||
if cnum == 1:
|
||||
spec_str = " program"
|
||||
else:
|
||||
spec_str = " programs"
|
||||
|
||||
clients_string = "Serving : "+str(cnum)+spec_str
|
||||
elif name.startswith("I2PInterface["):
|
||||
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
|
||||
cnum = clients
|
||||
if cnum == 1:
|
||||
spec_str = " connected I2P endpoint"
|
||||
else:
|
||||
spec_str = " connected I2P endpoints"
|
||||
|
||||
clients_string = "Peers : "+str(cnum)+spec_str
|
||||
else:
|
||||
clients_string = ""
|
||||
else:
|
||||
spec_str = " programs"
|
||||
clients_string = "Clients : "+str(clients)
|
||||
|
||||
clients_string = "Serving : "+str(cnum)+spec_str
|
||||
else:
|
||||
clients_string = "Clients : "+str(clients)
|
||||
clients = None
|
||||
|
||||
else:
|
||||
clients = None
|
||||
print(" {n}".format(n=ifstat["name"]))
|
||||
|
||||
print(" {n}".format(n=ifstat["name"]))
|
||||
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
||||
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
||||
|
||||
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
||||
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 clients != None:
|
||||
print(" "+clients_string)
|
||||
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
||||
print(" Mode : {mode}".format(mode=modestr))
|
||||
|
||||
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
||||
print(" Mode : {mode}".format(mode=modestr))
|
||||
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||
|
||||
if "peers" in ifstat and ifstat["peers"] != None:
|
||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||
|
||||
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||
|
||||
if "peers" in ifstat and ifstat["peers"] != None:
|
||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||
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))
|
||||
|
||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||
|
||||
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))
|
||||
|
||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||
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:
|
||||
aqn = ifstat["announce_queue"]
|
||||
if aqn == 1:
|
||||
print(" Queued : {np} announce".format(np=aqn))
|
||||
else:
|
||||
print(" Queued : {np} announces".format(np=aqn))
|
||||
|
||||
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
||||
if "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))
|
||||
else:
|
||||
print(" Queued : {np} announces".format(np=aqn))
|
||||
|
||||
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")
|
||||
print("\n Reticulum Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" is running")
|
||||
|
||||
print("")
|
||||
|
||||
|
||||
@@ -0,0 +1,697 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2016-2022 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 subprocess
|
||||
import argparse
|
||||
import shlex
|
||||
import time
|
||||
import sys
|
||||
import tty
|
||||
import os
|
||||
|
||||
from RNS._version import __version__
|
||||
|
||||
APP_NAME = "rnx"
|
||||
identity = None
|
||||
reticulum = None
|
||||
allow_all = False
|
||||
allowed_identity_hashes = []
|
||||
|
||||
def prepare_identity(identity_path):
|
||||
global identity
|
||||
if identity_path == None:
|
||||
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
||||
|
||||
if os.path.isfile(identity_path):
|
||||
identity = RNS.Identity.from_file(identity_path)
|
||||
|
||||
if identity == None:
|
||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||
identity = RNS.Identity()
|
||||
identity.to_file(identity_path)
|
||||
|
||||
def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed = [], print_identity = False, disable_auth = None, disable_announce=False):
|
||||
global identity, allow_all, allowed_identity_hashes, reticulum
|
||||
|
||||
targetloglevel = 3+verbosity-quietness
|
||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||
|
||||
prepare_identity(identitypath)
|
||||
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "execute")
|
||||
|
||||
if print_identity:
|
||||
print("Identity : "+str(identity))
|
||||
print("Listening on : "+RNS.prettyhexrep(destination.hash))
|
||||
exit(0)
|
||||
|
||||
if disable_auth:
|
||||
allow_all = True
|
||||
else:
|
||||
if allowed != None:
|
||||
for a in allowed:
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(a) != dest_len:
|
||||
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(a)
|
||||
allowed_identity_hashes.append(destination_hash)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit(1)
|
||||
|
||||
if len(allowed_identity_hashes) < 1 and not disable_auth:
|
||||
print("Warning: No allowed identities configured, rncx will not accept any commands!")
|
||||
|
||||
destination.set_link_established_callback(command_link_established)
|
||||
|
||||
if not allow_all:
|
||||
destination.register_request_handler(
|
||||
path = "command",
|
||||
response_generator = execute_received_command,
|
||||
allow = RNS.Destination.ALLOW_LIST,
|
||||
allowed_list = allowed_identity_hashes
|
||||
)
|
||||
else:
|
||||
destination.register_request_handler(
|
||||
path = "command",
|
||||
response_generator = execute_received_command,
|
||||
allow = RNS.Destination.ALLOW_ALL,
|
||||
)
|
||||
|
||||
RNS.log("rnx listening for commands on "+RNS.prettyhexrep(destination.hash))
|
||||
|
||||
if not disable_announce:
|
||||
destination.announce()
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
def command_link_established(link):
|
||||
link.set_remote_identified_callback(initiator_identified)
|
||||
link.set_link_closed_callback(command_link_closed)
|
||||
RNS.log("Command link "+str(link)+" established")
|
||||
|
||||
def command_link_closed(link):
|
||||
RNS.log("Command link "+str(link)+" closed")
|
||||
|
||||
def initiator_identified(link, identity):
|
||||
global allow_all
|
||||
RNS.log("Initiator of link "+str(link)+" identified as "+RNS.prettyhexrep(identity.hash))
|
||||
if not allow_all and not identity.hash in allowed_identity_hashes:
|
||||
RNS.log("Identity "+RNS.prettyhexrep(identity.hash)+" not allowed, tearing down link")
|
||||
link.teardown()
|
||||
|
||||
def execute_received_command(path, data, request_id, remote_identity, requested_at):
|
||||
command = data[0].decode("utf-8") # Command to execute
|
||||
timeout = data[1] # Timeout in seconds
|
||||
o_limit = data[2] # Size limit for stdout
|
||||
e_limit = data[3] # Size limit for stderr
|
||||
stdin = data[4] # Data passed to stdin
|
||||
|
||||
if remote_identity != None:
|
||||
RNS.log("Executing command ["+command+"] for "+RNS.prettyhexrep(remote_identity.hash))
|
||||
else:
|
||||
RNS.log("Executing command ["+command+"] for unknown requestor")
|
||||
|
||||
result = [
|
||||
False, # 0: Command was executed
|
||||
None, # 1: Return value
|
||||
None, # 2: Stdout
|
||||
None, # 3: Stderr
|
||||
None, # 4: Total stdout length
|
||||
None, # 5: Total stderr length
|
||||
time.time(), # 6: Started
|
||||
None, # 7: Concluded
|
||||
]
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(shlex.split(command), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
result[0] = True
|
||||
|
||||
except Exception as e:
|
||||
result[0] = False
|
||||
return result
|
||||
|
||||
stdout = b""
|
||||
stderr = b""
|
||||
timed_out = False
|
||||
|
||||
if stdin != None:
|
||||
process.stdin.write(stdin)
|
||||
|
||||
while True:
|
||||
try:
|
||||
stdout, stderr = process.communicate(timeout=1)
|
||||
if process.poll() != None:
|
||||
break
|
||||
|
||||
if len(stdout) > 0:
|
||||
print(str(stdout))
|
||||
sys.stdout.flush()
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
|
||||
if timeout != None and time.time() > result[6]+timeout:
|
||||
RNS.log("Command ["+command+"] timed out and is being killed...")
|
||||
process.terminate()
|
||||
process.wait()
|
||||
if process.poll() != None:
|
||||
stdout, stderr = process.communicate()
|
||||
else:
|
||||
stdout = None
|
||||
stderr = None
|
||||
|
||||
break
|
||||
|
||||
if timeout != None and time.time() < result[6]+timeout:
|
||||
result[7] = time.time()
|
||||
|
||||
# Deliver result
|
||||
result[1] = process.returncode
|
||||
|
||||
if o_limit != None and len(stdout) > o_limit:
|
||||
if o_limit == 0:
|
||||
result[2] = b""
|
||||
else:
|
||||
result[2] = stdout[0:o_limit]
|
||||
else:
|
||||
result[2] = stdout
|
||||
|
||||
if e_limit != None and len(stderr) > e_limit:
|
||||
if e_limit == 0:
|
||||
result[3] = b""
|
||||
else:
|
||||
result[3] = stderr[0:e_limit]
|
||||
else:
|
||||
result[3] = stderr
|
||||
|
||||
result[4] = len(stdout)
|
||||
result[5] = len(stderr)
|
||||
|
||||
if timed_out:
|
||||
RNS.log("Command timed out")
|
||||
return result
|
||||
|
||||
if remote_identity != None:
|
||||
RNS.log("Delivering result of command ["+str(command)+"] to "+RNS.prettyhexrep(remote_identity.hash))
|
||||
else:
|
||||
RNS.log("Delivering result of command ["+str(command)+"] to unknown requestor")
|
||||
|
||||
return result
|
||||
|
||||
def spin(until=None, msg=None, timeout=None):
|
||||
i = 0
|
||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||
if timeout != None:
|
||||
timeout = time.time()+timeout
|
||||
|
||||
print(msg+" ", end=" ")
|
||||
while (timeout == None or time.time()<timeout) and not until():
|
||||
time.sleep(0.1)
|
||||
print(("\b\b"+syms[i]+" "), end="")
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
print("\r"+" "*len(msg)+" \r", end="")
|
||||
|
||||
if timeout != None and time.time() > timeout:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
current_progress = 0.0
|
||||
stats = []
|
||||
speed = 0.0
|
||||
def spin_stat(until=None, timeout=None):
|
||||
global current_progress, response_transfer_size, speed
|
||||
i = 0
|
||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||
if timeout != None:
|
||||
timeout = time.time()+timeout
|
||||
|
||||
while (timeout == None or time.time()<timeout) and not until():
|
||||
time.sleep(0.1)
|
||||
prg = current_progress
|
||||
percent = round(prg * 100.0, 1)
|
||||
stat_str = str(percent)+"% - " + size_str(int(prg*response_transfer_size)) + " of " + size_str(response_transfer_size) + " - " +size_str(speed, "b")+"ps"
|
||||
print("\r \rReceiving result "+syms[i]+" "+stat_str, end=" ")
|
||||
|
||||
sys.stdout.flush()
|
||||
i = (i+1)%len(syms)
|
||||
|
||||
print("\r \r", end="")
|
||||
|
||||
if timeout != None and time.time() > timeout:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def remote_execution_done(request_receipt):
|
||||
pass
|
||||
|
||||
def remote_execution_progress(request_receipt):
|
||||
stats_max = 32
|
||||
global current_progress, response_transfer_size, speed
|
||||
current_progress = request_receipt.progress
|
||||
response_transfer_size = request_receipt.response_transfer_size
|
||||
now = time.time()
|
||||
got = current_progress*response_transfer_size
|
||||
entry = [now, got]
|
||||
stats.append(entry)
|
||||
while len(stats) > stats_max:
|
||||
stats.pop(0)
|
||||
|
||||
span = now - stats[0][0]
|
||||
if span == 0:
|
||||
speed = 0
|
||||
else:
|
||||
diff = got - stats[0][1]
|
||||
speed = diff/span
|
||||
|
||||
link = None
|
||||
listener_destination = None
|
||||
remote_exec_grace = 2.0
|
||||
def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detailed = False, mirror = False, noid = False, destination = None, command = None, stdin = None, stdoutl = None, stderrl = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, result_timeout = None, interactive = False):
|
||||
global identity, reticulum, link, listener_destination, remote_exec_grace
|
||||
|
||||
try:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(destination) != dest_len:
|
||||
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
destination_hash = bytes.fromhex(destination)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid destination entered. Check your input.")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
exit(241)
|
||||
|
||||
if reticulum == None:
|
||||
targetloglevel = 3+verbosity-quietness
|
||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||
|
||||
if identity == None:
|
||||
prepare_identity(identitypath)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
RNS.Transport.request_path(destination_hash)
|
||||
if not spin(until=lambda: RNS.Transport.has_path(destination_hash), msg="Path to "+RNS.prettyhexrep(destination_hash)+" requested", timeout=timeout):
|
||||
print("Path not found")
|
||||
exit(242)
|
||||
|
||||
if listener_destination == None:
|
||||
listener_identity = RNS.Identity.recall(destination_hash)
|
||||
listener_destination = RNS.Destination(
|
||||
listener_identity,
|
||||
RNS.Destination.OUT,
|
||||
RNS.Destination.SINGLE,
|
||||
APP_NAME,
|
||||
"execute"
|
||||
)
|
||||
|
||||
if link == None or link.status == RNS.Link.CLOSED or link.status == RNS.Link.PENDING:
|
||||
link = RNS.Link(listener_destination)
|
||||
|
||||
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg="Establishing link with "+RNS.prettyhexrep(destination_hash), timeout=timeout):
|
||||
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
|
||||
exit(243)
|
||||
|
||||
if not noid:
|
||||
link.identify(identity)
|
||||
|
||||
if stdin != None:
|
||||
stdin = stdin.encode("utf-8")
|
||||
|
||||
request_data = [
|
||||
command.encode("utf-8"), # Command to execute
|
||||
timeout, # Timeout in seconds
|
||||
stdoutl, # Size limit for stdout
|
||||
stderrl, # Size limit for stderr
|
||||
stdin, # Data passed to stdin
|
||||
]
|
||||
|
||||
# TODO: Tune
|
||||
rexec_timeout = timeout+link.rtt*4+remote_exec_grace
|
||||
|
||||
request_receipt = link.request(
|
||||
path="command",
|
||||
data=request_data,
|
||||
response_callback=remote_execution_done,
|
||||
failed_callback=remote_execution_done,
|
||||
progress_callback=remote_execution_progress,
|
||||
timeout=rexec_timeout
|
||||
)
|
||||
|
||||
spin(
|
||||
until=lambda:link.status == RNS.Link.CLOSED or (request_receipt.status != RNS.RequestReceipt.FAILED and request_receipt.status != RNS.RequestReceipt.SENT),
|
||||
msg="Sending execution request",
|
||||
timeout=rexec_timeout+0.5
|
||||
)
|
||||
|
||||
if link.status == RNS.Link.CLOSED:
|
||||
print("Could not request remote execution, link was closed")
|
||||
exit(244)
|
||||
|
||||
if request_receipt.status == RNS.RequestReceipt.FAILED:
|
||||
print("Could not request remote execution")
|
||||
exit(244)
|
||||
|
||||
spin(
|
||||
until=lambda:request_receipt.status != RNS.RequestReceipt.DELIVERED,
|
||||
msg="Command delivered, awaiting result",
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
if request_receipt.status == RNS.RequestReceipt.FAILED:
|
||||
print("No result was received")
|
||||
exit(245)
|
||||
|
||||
spin_stat(
|
||||
until=lambda:request_receipt.status != RNS.RequestReceipt.RECEIVING,
|
||||
timeout=result_timeout
|
||||
)
|
||||
|
||||
if request_receipt.status == RNS.RequestReceipt.FAILED:
|
||||
print("Receiving result failed")
|
||||
exit(246)
|
||||
|
||||
if request_receipt.response != None:
|
||||
try:
|
||||
executed = request_receipt.response[0]
|
||||
retval = request_receipt.response[1]
|
||||
stdout = request_receipt.response[2]
|
||||
stderr = request_receipt.response[3]
|
||||
outlen = request_receipt.response[4]
|
||||
errlen = request_receipt.response[5]
|
||||
started = request_receipt.response[6]
|
||||
concluded = request_receipt.response[7]
|
||||
|
||||
except Exception as e:
|
||||
print("Received invalid result")
|
||||
exit(247)
|
||||
|
||||
if executed:
|
||||
if detailed:
|
||||
if stdout != None and len(stdout) > 0:
|
||||
print(stdout.decode("utf-8"), end="")
|
||||
if stderr != None and len(stderr) > 0:
|
||||
print(stderr.decode("utf-8"), file=sys.stderr, end="")
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
print("\n--- End of remote output, rnx done ---")
|
||||
if started != None and concluded != None:
|
||||
cmd_duration = round(concluded - started, 3)
|
||||
print("Remote command execution took "+str(cmd_duration)+" seconds")
|
||||
|
||||
total_size = request_receipt.response_size
|
||||
if request_receipt.request_size != None:
|
||||
total_size += request_receipt.request_size
|
||||
|
||||
transfer_duration = round(request_receipt.response_concluded_at - request_receipt.sent_at - cmd_duration, 3)
|
||||
if transfer_duration == 1:
|
||||
tdstr = " in 1 second"
|
||||
elif transfer_duration < 10:
|
||||
tdstr = " in "+str(transfer_duration)+" seconds"
|
||||
else:
|
||||
tdstr = " in "+pretty_time(transfer_duration)
|
||||
|
||||
spdstr = ", effective rate "+size_str(total_size/transfer_duration, "b")+"ps"
|
||||
|
||||
print("Transferred "+size_str(total_size)+tdstr+spdstr)
|
||||
|
||||
if outlen != None and stdout != None:
|
||||
if len(stdout) < outlen:
|
||||
tstr = ", "+str(len(stdout))+" bytes displayed"
|
||||
else:
|
||||
tstr = ""
|
||||
print("Remote wrote "+str(outlen)+" bytes to stdout"+tstr)
|
||||
|
||||
if errlen != None and stderr != None:
|
||||
if len(stderr) < errlen:
|
||||
tstr = ", "+str(len(stderr))+" bytes displayed"
|
||||
else:
|
||||
tstr = ""
|
||||
print("Remote wrote "+str(errlen)+" bytes to stderr"+tstr)
|
||||
|
||||
else:
|
||||
if stdout != None and len(stdout) > 0:
|
||||
print(stdout.decode("utf-8"), end="")
|
||||
if stderr != None and len(stderr) > 0:
|
||||
print(stderr.decode("utf-8"), file=sys.stderr, end="")
|
||||
|
||||
|
||||
if (stdoutl != 0 and len(stdout) < outlen) or (stderrl != 0 and len(stderr) < errlen):
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
print("\nOutput truncated before being returned:")
|
||||
if len(stdout) != 0 and len(stdout) < outlen:
|
||||
print(" stdout truncated to "+str(len(stdout))+" bytes")
|
||||
if len(stderr) != 0 and len(stderr) < errlen:
|
||||
print(" stderr truncated to "+str(len(stderr))+" bytes")
|
||||
else:
|
||||
print("Remote could not execute command")
|
||||
if interactive:
|
||||
return
|
||||
else:
|
||||
exit(248)
|
||||
else:
|
||||
print("No response")
|
||||
exit(249)
|
||||
|
||||
try:
|
||||
if not interactive:
|
||||
link.teardown()
|
||||
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if not interactive and mirror:
|
||||
if request_receipt.response[1] != None:
|
||||
exit(request_receipt.response[1])
|
||||
else:
|
||||
exit(240)
|
||||
else:
|
||||
if interactive:
|
||||
if mirror:
|
||||
return request_receipt.response[1]
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
exit(0)
|
||||
|
||||
def main():
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description="Reticulum Remote Execution Utility")
|
||||
parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the listener", type=str)
|
||||
parser.add_argument("command", nargs="?", default=None, help="command to be execute", type=str)
|
||||
parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0, help="increase verbosity")
|
||||
parser.add_argument('-q', '--quiet', action='count', default=0, help="decrease verbosity")
|
||||
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
|
||||
parser.add_argument("-l", '--listen', action='store_true', default=False, help="listen for incoming commands")
|
||||
parser.add_argument('-i', metavar="identity", action='store', dest="identity", default=None, help="path to identity to use", type=str)
|
||||
parser.add_argument("-x", '--interactive', action='store_true', default=False, help="enter interactive mode")
|
||||
parser.add_argument("-b", '--no-announce', action='store_true', default=False, help="don't announce at program start")
|
||||
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="accept from this identity", type=str)
|
||||
parser.add_argument('-n', '--noauth', action='store_true', default=False, help="accept files from anyone")
|
||||
parser.add_argument('-N', '--noid', action='store_true', default=False, help="don't identify to listener")
|
||||
parser.add_argument("-d", '--detailed', action='store_true', default=False, help="show detailed result output")
|
||||
parser.add_argument("-m", action='store_true', dest="mirror", default=False, help="mirror exit code of remote command")
|
||||
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="connect and request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
||||
parser.add_argument("-W", action="store", metavar="seconds", type=float, help="max result download time", default=None)
|
||||
parser.add_argument("--stdin", action='store', default=None, help="pass input to stdin", type=str)
|
||||
parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int)
|
||||
parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int)
|
||||
parser.add_argument("--version", action="version", version="rnx {version}".format(version=__version__))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.listen or args.print_identity:
|
||||
listen(
|
||||
configdir = args.config,
|
||||
identitypath = args.identity,
|
||||
verbosity=args.verbose,
|
||||
quietness=args.quiet,
|
||||
allowed = args.allowed,
|
||||
print_identity=args.print_identity,
|
||||
disable_auth=args.noauth,
|
||||
disable_announce=args.no_announce,
|
||||
)
|
||||
|
||||
elif args.destination != None and args.command != None:
|
||||
execute(
|
||||
configdir = args.config,
|
||||
identitypath = args.identity,
|
||||
verbosity = args.verbose,
|
||||
quietness = args.quiet,
|
||||
detailed = args.detailed,
|
||||
mirror = args.mirror,
|
||||
noid = args.noid,
|
||||
destination = args.destination,
|
||||
command = args.command,
|
||||
stdin = args.stdin,
|
||||
stdoutl = args.stdout,
|
||||
stderrl = args.stderr,
|
||||
timeout = args.w,
|
||||
result_timeout = args.W,
|
||||
interactive = args.interactive,
|
||||
)
|
||||
|
||||
if args.destination != None and args.interactive:
|
||||
# command_history_max = 5000
|
||||
# command_history = []
|
||||
# command_current = ""
|
||||
# history_idx = 0
|
||||
# tty.setcbreak(sys.stdin.fileno())
|
||||
|
||||
code = None
|
||||
while True:
|
||||
try:
|
||||
cstr = str(code) if code and code != 0 else ""
|
||||
prompt = cstr+"> "
|
||||
print(prompt,end="")
|
||||
|
||||
# cmdbuf = b""
|
||||
# while True:
|
||||
# ch = sys.stdin.read(1)
|
||||
# cmdbuf += ch.encode("utf-8")
|
||||
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
|
||||
|
||||
command = input()
|
||||
if command.lower() == "exit" or command.lower() == "quit":
|
||||
exit(0)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
exit(0)
|
||||
except EOFError:
|
||||
exit(0)
|
||||
|
||||
if command.lower() == "clear":
|
||||
print('\033c', end='')
|
||||
|
||||
# command_history.append(command)
|
||||
# while len(command_history) > command_history_max:
|
||||
# command_history.pop(0)
|
||||
|
||||
else:
|
||||
code = execute(
|
||||
configdir = args.config,
|
||||
identitypath = args.identity,
|
||||
verbosity = args.verbose,
|
||||
quietness = args.quiet,
|
||||
detailed = args.detailed,
|
||||
mirror = args.mirror,
|
||||
noid = args.noid,
|
||||
destination = args.destination,
|
||||
command = command,
|
||||
stdin = None,
|
||||
stdoutl = args.stdout,
|
||||
stderrl = args.stderr,
|
||||
timeout = args.w,
|
||||
result_timeout = args.W,
|
||||
interactive = True,
|
||||
)
|
||||
|
||||
else:
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# tty.setnocbreak(sys.stdin.fileno())
|
||||
print("")
|
||||
if link != None:
|
||||
link.teardown()
|
||||
exit()
|
||||
|
||||
def size_str(num, suffix='B'):
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
|
||||
if suffix == 'b':
|
||||
num *= 8
|
||||
units = ['','K','M','G','T','P','E','Z']
|
||||
last_unit = 'Y'
|
||||
|
||||
for unit in units:
|
||||
if abs(num) < 1000.0:
|
||||
if unit == "":
|
||||
return "%.0f %s%s" % (num, unit, suffix)
|
||||
else:
|
||||
return "%.2f %s%s" % (num, unit, suffix)
|
||||
num /= 1000.0
|
||||
|
||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||
|
||||
def pretty_time(time, verbose=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)
|
||||
|
||||
ss = "" if seconds == 1 else "s"
|
||||
sm = "" if minutes == 1 else "s"
|
||||
sh = "" if hours == 1 else "s"
|
||||
sd = "" if days == 1 else "s"
|
||||
|
||||
components = []
|
||||
if days > 0:
|
||||
components.append(str(days)+" day"+sd if verbose else str(days)+"d")
|
||||
|
||||
if hours > 0:
|
||||
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h")
|
||||
|
||||
if minutes > 0:
|
||||
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m")
|
||||
|
||||
if seconds > 0:
|
||||
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
|
||||
|
||||
i = 0
|
||||
tstr = ""
|
||||
for c in components:
|
||||
i += 1
|
||||
if i == 1:
|
||||
pass
|
||||
elif i < len(components):
|
||||
tstr += ", "
|
||||
elif i == len(components):
|
||||
tstr += " and "
|
||||
|
||||
tstr += c
|
||||
|
||||
return tstr
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "0.3.5"
|
||||
__version__ = "0.3.7"
|
||||
|
||||
Vendored
+1
-1
@@ -25,7 +25,7 @@ async def get_sam_socket(sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||
:param loop: (optional) event loop instance
|
||||
:return: A (reader, writer) pair
|
||||
"""
|
||||
reader, writer = await asyncio.open_connection(*sam_address, loop=loop)
|
||||
reader, writer = await asyncio.open_connection(*sam_address)
|
||||
writer.write(sam.hello("3.1", "3.1"))
|
||||
reply = parse_reply(await reader.readline())
|
||||
if reply.ok:
|
||||
|
||||
Vendored
+47
-16
@@ -85,17 +85,35 @@ class ClientTunnel(I2PTunnel):
|
||||
"""A coroutine used to run the tunnel"""
|
||||
await self._pre_run()
|
||||
|
||||
self.status = { "setup_ran": False, "setup_failed": False, "exception": None, "connect_tasks": [] }
|
||||
async def handle_client(client_reader, client_writer):
|
||||
"""Handle local client connection"""
|
||||
remote_reader, remote_writer = await aiosam.stream_connect(
|
||||
self.session_name, self.remote_destination,
|
||||
sam_address=self.sam_address, loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
||||
loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
||||
loop=self.loop)
|
||||
try:
|
||||
sc_task = aiosam.stream_connect(
|
||||
self.session_name, self.remote_destination,
|
||||
sam_address=self.sam_address, loop=self.loop)
|
||||
self.status["connect_tasks"].append(sc_task)
|
||||
|
||||
remote_reader, remote_writer = await sc_task
|
||||
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
||||
loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
||||
loop=self.loop)
|
||||
|
||||
self.server = await asyncio.start_server(handle_client, *self.local_address, loop=self.loop)
|
||||
except Exception as e:
|
||||
self.status["setup_ran"] = True
|
||||
self.status["setup_failed"] = True
|
||||
self.status["exception"] = e
|
||||
|
||||
|
||||
try:
|
||||
self.server = await asyncio.start_server(handle_client, *self.local_address)
|
||||
self.status["setup_ran"] = True
|
||||
|
||||
except Exception as e:
|
||||
self.status["setup_ran"] = True
|
||||
self.status["setup_failed"] = True
|
||||
self.status["exception"] = e
|
||||
|
||||
def stop(self):
|
||||
super().stop()
|
||||
@@ -117,26 +135,38 @@ class ServerTunnel(I2PTunnel):
|
||||
"""A coroutine used to run the tunnel"""
|
||||
await self._pre_run()
|
||||
|
||||
self.status = { "setup_ran": False, "setup_failed": False, "exception": None, "connect_tasks": [] }
|
||||
async def handle_client(incoming, client_reader, client_writer):
|
||||
# data and dest may come in one chunk
|
||||
dest, data = incoming.split(b"\n", 1)
|
||||
remote_destination = sam.Destination(dest.decode())
|
||||
logger.debug("{} client connected: {}.b32.i2p".format(
|
||||
self.session_name, remote_destination.base32))
|
||||
try:
|
||||
# data and dest may come in one chunk
|
||||
dest, data = incoming.split(b"\n", 1)
|
||||
remote_destination = sam.Destination(dest.decode())
|
||||
logger.debug("{} client connected: {}.b32.i2p".format(
|
||||
self.session_name, remote_destination.base32))
|
||||
|
||||
except Exception as e:
|
||||
self.status["exception"] = e
|
||||
self.status["setup_failed"] = True
|
||||
|
||||
try:
|
||||
remote_reader, remote_writer = await asyncio.wait_for(
|
||||
sc_task = asyncio.wait_for(
|
||||
asyncio.open_connection(
|
||||
host=self.local_address[0],
|
||||
port=self.local_address[1], loop=self.loop),
|
||||
timeout=5, loop=self.loop)
|
||||
port=self.local_address[1]),
|
||||
timeout=5)
|
||||
self.status["connect_tasks"].append(sc_task)
|
||||
|
||||
remote_reader, remote_writer = await sc_task
|
||||
if data: remote_writer.write(data)
|
||||
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
||||
loop=self.loop)
|
||||
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
||||
loop=self.loop)
|
||||
|
||||
except ConnectionRefusedError:
|
||||
client_writer.close()
|
||||
self.status["exception"] = e
|
||||
self.status["setup_failed"] = True
|
||||
|
||||
async def server_loop():
|
||||
try:
|
||||
@@ -151,6 +181,7 @@ class ServerTunnel(I2PTunnel):
|
||||
pass
|
||||
|
||||
self.server_loop = asyncio.ensure_future(server_loop(), loop=self.loop)
|
||||
self.status["setup_ran"] = True
|
||||
|
||||
def stop(self):
|
||||
super().stop()
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: d4939f555bda9c488f47cdcede85949d
|
||||
config: 3ea52ff0bfd9431c8886e9a105e9d835
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -19,71 +19,6 @@ types, have a look at the :ref:`Building Networks<networks-main>` chapter of thi
|
||||
manual.
|
||||
|
||||
|
||||
.. _interfaces-options:
|
||||
|
||||
Common Interface Options
|
||||
========================
|
||||
|
||||
A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.
|
||||
|
||||
|
||||
* | The ``enabled`` option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to ``False``. For any
|
||||
interface to be brought up, the ``enabled`` option
|
||||
must be set to ``True`` or ``Yes``.
|
||||
|
||||
* | The ``mode`` option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.
|
||||
|
||||
- The default value is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is available.
|
||||
|
||||
- In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.
|
||||
|
||||
* | The ``outgoing`` option sets whether an interface is allowed
|
||||
to transmit. Defaults to ``True``. If set to ``False`` or ``No``
|
||||
the interface will only receive data, and never transmit.
|
||||
|
||||
* | The ``network_name`` option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.
|
||||
|
||||
* | The ``passphrase`` option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
``network_name`` option, or be used alone.
|
||||
|
||||
* | The ``ifac_size`` option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.
|
||||
|
||||
* | The ``announce_cap`` option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between ``1`` and ``100``.
|
||||
|
||||
* | The ``bitrate`` option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn't report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the ``bitrate``
|
||||
option, to set the interface speed in *bits per second*.
|
||||
|
||||
|
||||
.. _interfaces-auto:
|
||||
|
||||
Auto Interface
|
||||
@@ -165,8 +100,8 @@ at.
|
||||
To use the I2P interface, you must have an I2P router running
|
||||
on your system. The easiest way to acheive this is to download and
|
||||
install the `latest release <https://github.com/PurpleI2P/i2pd/releases/latest>`_
|
||||
of the ``ì2pd`` package. For more details about I2P, see the
|
||||
`geti2p.net website <https://geti2p.net/en/about/intro>`_.`
|
||||
of the ``i2pd`` package. For more details about I2P, see the
|
||||
`geti2p.net website <https://geti2p.net/en/about/intro>`_.
|
||||
|
||||
When an I2P router is running on your system, you can simply add
|
||||
an I2P interface to reticulum:
|
||||
@@ -260,6 +195,9 @@ you must use the i2p_tunneled option:
|
||||
listen_port = 5001
|
||||
i2p_tunneled = yes
|
||||
|
||||
In almost all cases, it is easier to use the dedicated ``I2PInterface``, but for complete
|
||||
control, and using I2P routers running on external systems, this option also exists.
|
||||
|
||||
.. _interfaces-tcpc:
|
||||
|
||||
TCP Client Interface
|
||||
@@ -269,6 +207,10 @@ To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.
|
||||
|
||||
The TCP interface types can also tolerate intermittency in the IP link layer.
|
||||
This means that Reticulum will gracefully handle IP links that go up and down,
|
||||
and restore connectivity after a failure, once the other end of a TCP interface reappears.
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface. The
|
||||
@@ -329,8 +271,8 @@ with all other peers on a local area network.
|
||||
*Please Note!* Using broadcast UDP traffic has performance implications,
|
||||
especially on WiFi. If your goal is simply to enable easy communication
|
||||
with all peers in your local ethernet broadcast domain, the
|
||||
:ref:`Auto Interface<interfaces-auto>` performs better, and is just as
|
||||
easy to use.
|
||||
:ref:`Auto Interface<interfaces-auto>` performs better, and is even
|
||||
easier to use.
|
||||
|
||||
The below example is enabled by default on new Reticulum installations,
|
||||
as it provides an easy way to get started and to test Reticulum on a
|
||||
@@ -341,7 +283,7 @@ pre-existing LAN.
|
||||
# This example enables communication with other
|
||||
# local Reticulum peers over UDP.
|
||||
|
||||
[[Default UDP Interface]]
|
||||
[[UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = True
|
||||
|
||||
@@ -461,6 +403,31 @@ directly over a wire-pair, or for using devices such as data radios and lasers.
|
||||
parity = none
|
||||
stopbits = 1
|
||||
|
||||
.. _interfaces-pipe:
|
||||
|
||||
Pipe Interface
|
||||
==============
|
||||
|
||||
Using this interface, reticulum can use any program as an interface via `stdin` and
|
||||
`stdout`. This can be used to easily create virtual interfaces, or to interface with
|
||||
custom hardware or other systems.
|
||||
|
||||
.. code::
|
||||
|
||||
[[Pipe Interface]]
|
||||
type = PipeInterface
|
||||
interface_enabled = True
|
||||
|
||||
# External command to execute
|
||||
command = netcat -l 5757
|
||||
|
||||
# Optional respawn delay, in seconds
|
||||
respawn_delay = 5
|
||||
|
||||
Reticulum will write all packets to `stdin` of the ``command`` option, and will
|
||||
continously read and scan its `stdout` for Reticulum packets. If ``EOF`` is reached,
|
||||
Reticulum will try to respawn the program after waiting for ``respawn_interval`` seconds.
|
||||
|
||||
.. _interfaces-kiss:
|
||||
|
||||
KISS Interface
|
||||
@@ -578,3 +545,208 @@ beaconing functionality described above.
|
||||
# This is useful for modems with a
|
||||
# small internal packet buffer.
|
||||
flow_control = false
|
||||
|
||||
.. _interfaces-options:
|
||||
|
||||
Common Interface Options
|
||||
========================
|
||||
|
||||
A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.
|
||||
|
||||
|
||||
* | The ``enabled`` option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to ``False``. For any
|
||||
interface to be brought up, the ``enabled`` option
|
||||
must be set to ``True`` or ``Yes``.
|
||||
|
||||
* | The ``mode`` option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.
|
||||
|
||||
- The default value is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is available.
|
||||
|
||||
- In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.
|
||||
|
||||
* | The ``outgoing`` option sets whether an interface is allowed
|
||||
to transmit. Defaults to ``True``. If set to ``False`` or ``No``
|
||||
the interface will only receive data, and never transmit.
|
||||
|
||||
* | The ``network_name`` option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.
|
||||
|
||||
* | The ``passphrase`` option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
``network_name`` option, or be used alone.
|
||||
|
||||
* | The ``ifac_size`` option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.
|
||||
|
||||
* | The ``announce_cap`` option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between ``1`` and ``100``.
|
||||
|
||||
*If an interface exceeds its announce cap, it will queue announces
|
||||
for later transmission. Reticulum will always prioritise propagating
|
||||
announces from nearby nodes first. This ensures that the local
|
||||
topology is prioritised, and that slow networks are not overwhelmed
|
||||
by interconnected fast networks.*
|
||||
|
||||
*Destinations that are rapidly re-announcing will be down-prioritised
|
||||
further. Trying to get "first-in-line" by announce spamming will have
|
||||
the exact opposite effect: Getting moved to the back of the queue every
|
||||
time a new announce from the excessively announcing destination is received.*
|
||||
|
||||
*This means that it is always beneficial to select a balanced
|
||||
announce rate, and not announce more often than is actually necesarry
|
||||
for your application to function.*
|
||||
|
||||
* | The ``bitrate`` option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn't report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the ``bitrate``
|
||||
option, to set the interface speed in *bits per second*.
|
||||
|
||||
|
||||
.. _interfaces-modes:
|
||||
|
||||
Interface Modes
|
||||
===============
|
||||
|
||||
The optional ``mode`` setting is available on all interfaces, and allows
|
||||
selecting the high-level behaviour of the interface from a number of modes.
|
||||
These modes affect how Reticulum selects paths in the network, how announces
|
||||
are propagated, how long paths are valid and how paths are discovered.
|
||||
|
||||
Configuring modes on interfaces is **not** strictly necessary, but can be useful
|
||||
when building or connecting to more complex networks. If your Reticulum
|
||||
instance is not running a Transport Node, it is rarely useful to configure
|
||||
interface modes, and in such cases interfaces should generally be left in
|
||||
the default mode.
|
||||
|
||||
* | The default mode is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is activated.
|
||||
|
||||
* | The ``gateway`` mode (or shorthand ``gw``) also has all
|
||||
discovery, meshing and transport functionality available,
|
||||
but will additionally try to discover unknown paths on
|
||||
behalf of other nodes residing on the ``gateway`` interface.
|
||||
If Reticulum receives a path request for an unknown
|
||||
destination, from a node on a ``gateway`` interface, it
|
||||
will try to discover this path via all other active interfaces,
|
||||
and forward the discovered path to the requestor if one is
|
||||
found.
|
||||
|
||||
| If you want to allow other nodes to widely resolve paths or connect
|
||||
to a network via an interface, it might be useful to put it in this
|
||||
mode. By creating a chain of ``gateway`` interfaces, other
|
||||
nodes will be able to immediately discover paths to any
|
||||
destination along the chain.
|
||||
|
||||
| *Please note!* It is the interface *facing the clients* that
|
||||
must be put into ``gateway`` mode for this to work, not
|
||||
the interface facing the wider network (for this, the ``boundary``
|
||||
mode can be useful, though).
|
||||
|
||||
* | In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. In addition, path
|
||||
requests from clients on the access point interface will
|
||||
be handled in the same way as the ``gateway`` interface.
|
||||
|
||||
| This mode is useful for creating interfaces that remain
|
||||
quiet, until someone actually starts using them. An example
|
||||
of this could be a radio interface serving a wide area,
|
||||
where users are expected to connect momentarily, use the
|
||||
network, and then disappear again.
|
||||
|
||||
* | The ``roaming`` mode should be used on interfaces that are
|
||||
roaming (physically mobile), seen from the perspective of
|
||||
other nodes in the network. As an example, if a vehicle is
|
||||
equipped with an external LoRa interface, and an internal,
|
||||
WiFi-based interface, that serves devices that are moving
|
||||
_with_ the vehicle, the external LoRa interface should be
|
||||
configured as ``roaming``, and the internal interface can
|
||||
be left in the default mode. With transport enabled, such
|
||||
a setup will allow all internal devices to reach each other,
|
||||
and all other devices that are available on the LoRa side
|
||||
of the network, when they are in range. Devices on the LoRa
|
||||
side of the network will also be able to reach devices
|
||||
internal to the vehicle, when it is in range. Paths via
|
||||
``roaming`` interfaces also expire faster.
|
||||
|
||||
* | The purpose of the ``boundary`` mode is to specify interfaces
|
||||
that establish connectivity with network segments that are
|
||||
significantly different than the one this node exists on.
|
||||
As an example, if a Reticulum instance is part of a LoRa-based
|
||||
network, but also has a high-speed connection to a
|
||||
public Transport Node available on the Internet, the interface
|
||||
connecting over the Internet should be set to ``boundary`` mode.
|
||||
|
||||
For a table describing the impact of all modes on announce propagation,
|
||||
please see the :ref:`Announce Propagation Rules<understanding-announcepropagation>` section.
|
||||
|
||||
.. _interfaces-announcerates:
|
||||
|
||||
Announce Rate Control
|
||||
=====================
|
||||
|
||||
The built-in announce control mechanisms and the default ``announce_cap``
|
||||
option described above are sufficient most of the time, but in some cases, especially on fast
|
||||
interfaces, it may be useful to control the target announce rate. Using the
|
||||
``announce_rate_target``, ``announce_rate_grace`` and ``announce_rate_penalty``
|
||||
options, this can be done on a per-interface basis, and moderates the *rate at
|
||||
which received announces are re-broadcasted to other interfaces*.
|
||||
|
||||
* | The ``announce_rate_target`` option sets the minimum amount of time,
|
||||
in seconds, that should pass between received announces, for any one
|
||||
destination. As an example, setting this value to ``3600`` means that
|
||||
announces *received* on this interface will only be re-transmitted and
|
||||
propagated to other interfaces once every hour, no matter how often they
|
||||
are received.
|
||||
|
||||
* | The optional ``announce_rate_grace`` defines the number of times a destination
|
||||
can violate the announce rate before the target rate is enforced.
|
||||
|
||||
* | The optional ``announce_rate_penalty`` configures an extra amount of
|
||||
time that is added to the normal rate target. As an example, if a penalty
|
||||
of ``7200`` seconds is defined, once the rate target is enforced, the
|
||||
destination in question will only have its announces propagated every
|
||||
3 hours, until it lowers its actual announce rate to within the target.
|
||||
|
||||
These mechanisms, in conjunction with the ``annouce_cap`` mechanisms mentioned
|
||||
above means that it is essential to select a balanced announce strategy for
|
||||
your destinations. The more balanced you can make this decision, the easier
|
||||
it will be for your destinations to make it into slower networks that many hops
|
||||
away. Or you can prioritise only reaching high-capacity networks with more frequent
|
||||
announces.
|
||||
|
||||
Current statistics and information about announce rates can be viewed using the
|
||||
``rnpath -r`` command.
|
||||
|
||||
It is important to note that there is no one right or wrong way to set up announce
|
||||
rates. Slower networks will naturally tend towards using less frequent announces to
|
||||
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.
|
||||
|
||||
|
||||
@@ -51,9 +51,8 @@ connected to any kind of computer or mobile device that Reticulum can run on.
|
||||
|
||||
The ultimate aim of Reticulum is to allow anyone to be their own network operator, and to make it
|
||||
cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks.
|
||||
Reticulum **is not** *one network*, it **is a tool** to build *thousands of networks*.
|
||||
|
||||
Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
Reticulum **is not** *one network*, it **is a tool** to build *thousands of networks*. Networks without
|
||||
kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
|
||||
.. _understanding-goals:
|
||||
@@ -802,3 +801,41 @@ Wire Format
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
|
||||
.. _understanding-announcepropagation:
|
||||
|
||||
Announce Propagation Rules
|
||||
--------------------------
|
||||
|
||||
The following table illustrates the rules for automatically propagating announces
|
||||
from one interface type to another, for all possible combinations. For the purpose
|
||||
of announce propagation, the *Full* and *Gateway* modes are identical.
|
||||
|
||||
.. image:: graphics/if_mode_graph_b.png
|
||||
|
||||
See the :ref:`Interface Modes<interfaces-modes>` section for a conceptual overview
|
||||
of the different interface modes, and how they are configured.
|
||||
|
||||
..
|
||||
(.. code-block:: text)
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼───> Full >───┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✓ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✕ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✕ ──┼────> AP >────┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Roaming >──┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✕ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Boundary >─┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
|
||||
@@ -19,9 +19,129 @@ instance is simply shared. This works for any number of programs running
|
||||
concurrently, and is very easy to use, but depending on your use case, there
|
||||
are other options.
|
||||
|
||||
Configuration & Data
|
||||
--------------------
|
||||
|
||||
A Reticulum stores all information that it needs to function in a single file-
|
||||
system directory. By default, this directory is ``~/.reticulum``, but you can
|
||||
use any directory you wish. You can also run multiple separate Reticulum
|
||||
instances on the same physical system, in complete isolation from each other,
|
||||
or connected together.
|
||||
|
||||
In most cases, a single physical system will only need to run one Reticulum
|
||||
instance. This can either be launched at boot, as a system service, or simply
|
||||
be brought up when a program needs it. In either case, any number of programs
|
||||
running on the same system will automatically share the same Reticulum instance,
|
||||
if the configuration allows for it, which it does by default.
|
||||
|
||||
The entire configuration of Reticulum is found in the ``~/.reticulum/config``
|
||||
file. When Reticulum is first started on a new system, a basic, functional
|
||||
configuration file is created. The default configuration looks like this:
|
||||
|
||||
.. code::
|
||||
|
||||
# This is the default Reticulum config file.
|
||||
# You should probably edit it to include any additional,
|
||||
# interfaces and settings you might need.
|
||||
|
||||
# Only the most basic options are included in this default
|
||||
# configuration. To see a more verbose, and much longer,
|
||||
# configuration example, you can run the command:
|
||||
# rnsd --exampleconfig
|
||||
|
||||
|
||||
[reticulum]
|
||||
|
||||
# If you enable Transport, your system will route traffic
|
||||
# for other peers, pass announces and serve path requests.
|
||||
# This should only be done for systems that are suited to
|
||||
# act as transport nodes, ie. if they are stationary and
|
||||
# always-on. This directive is optional and can be removed
|
||||
# for brevity.
|
||||
|
||||
enable_transport = False
|
||||
|
||||
|
||||
# By default, the first program to launch the Reticulum
|
||||
# Network Stack will create a shared instance, that other
|
||||
# programs can communicate with. Only the shared instance
|
||||
# opens all the configured interfaces directly, and other
|
||||
# local programs communicate with the shared instance over
|
||||
# a local socket. This is completely transparent to the
|
||||
# user, and should generally be turned on. This directive
|
||||
# is optional and can be removed for brevity.
|
||||
|
||||
share_instance = Yes
|
||||
|
||||
|
||||
# If you want to run multiple *different* shared instances
|
||||
# on the same system, you will need to specify different
|
||||
# shared instance ports for each. The defaults are given
|
||||
# below, and again, these options can be left out if you
|
||||
# don't need them.
|
||||
|
||||
shared_instance_port = 37428
|
||||
instance_control_port = 37429
|
||||
|
||||
|
||||
# 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
|
||||
# an optional directive, and can be left out for brevity.
|
||||
# This behaviour is disabled by default.
|
||||
|
||||
panic_on_interface_error = No
|
||||
|
||||
|
||||
[logging]
|
||||
# Valid log levels are 0 through 7:
|
||||
# 0: Log only critical information
|
||||
# 1: Log errors and lower log levels
|
||||
# 2: Log warnings and lower log levels
|
||||
# 3: Log notices and lower log levels
|
||||
# 4: Log info and lower (this is the default)
|
||||
# 5: Verbose logging
|
||||
# 6: Debug logging
|
||||
# 7: Extreme logging
|
||||
|
||||
loglevel = 4
|
||||
|
||||
|
||||
# The interfaces section defines the physical and virtual
|
||||
# interfaces Reticulum will use to communicate on. This
|
||||
# section will contain examples for a variety of interface
|
||||
# types. You can modify these or use them as a basis for
|
||||
# your own config, or simply remove the unused ones.
|
||||
|
||||
[interfaces]
|
||||
|
||||
# This interface enables communication with other
|
||||
# link-local Reticulum nodes over UDP. It does not
|
||||
# need any functional IP infrastructure like routers
|
||||
# or DHCP servers, but will require that at least link-
|
||||
# local IPv6 is enabled in your operating system, which
|
||||
# should be enabled by default in almost any OS. See
|
||||
# the Reticulum Manual for more configuration options.
|
||||
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
|
||||
If Reticulum infrastructure already exists locally, you probably don't need to
|
||||
change anything, and you may already be connected to a wider network. If not,
|
||||
you will probably need to add relevant *interfaces* to the configuration, in
|
||||
order to communicate with other systems. It is a good idea to read the comments
|
||||
and explanations in the above default config. It will teach you the basic
|
||||
concepts you need to understand to configure your network. Once you have done that,
|
||||
take a look at the :ref:`Interfaces<interfaces-main>` chapter of this manual.
|
||||
|
||||
Included Utility Programs
|
||||
-------------------------
|
||||
|
||||
Reticulum includes a range of useful utilities, both for managing your Reticulum
|
||||
networks, and for carrying out common tasks over Reticulum networks, such as
|
||||
transferring files to remote systems, and executing commands and programs remotely.
|
||||
|
||||
If you often use Reticulum from several different programs, or simply want
|
||||
Reticulum to stay available all the time, for example if you are hosting
|
||||
a transport node, you might want to run Reticulum as a separate service that
|
||||
@@ -30,8 +150,8 @@ other programs, applications and services can utilise.
|
||||
The rnsd Utility
|
||||
================
|
||||
|
||||
To do so is very easy. Simply run the included ``rnsd`` command. When ``rnsd``
|
||||
is running, it will keep all configured interfaces open, handle transport if
|
||||
It is very easy to run Reticulum as a service. Simply run the included ``rnsd`` command.
|
||||
When ``rnsd`` is running, it will keep all configured interfaces open, handle transport if
|
||||
it is enabled, and allow any other programs to immediately utilise the
|
||||
Reticulum network it is configured for.
|
||||
|
||||
@@ -135,21 +255,22 @@ destinations on the Reticulum network.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-d] [-w seconds] [-v]
|
||||
[destination]
|
||||
|
||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D] [-w seconds] [-v] [destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
|
||||
positional arguments:
|
||||
destination hexadecimal hash of the destination
|
||||
|
||||
destination hexadecimal hash of the destination
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-d, --drop remove the path to a destination
|
||||
-w seconds timeout before giving up
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-r, --rates show announce rate info
|
||||
-d, --drop remove the path to a destination
|
||||
-D, --drop-announces drop all queued announces
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
|
||||
|
||||
@@ -164,7 +285,7 @@ destinations will not have this option enabled, and will not be probable.
|
||||
.. code:: text
|
||||
|
||||
# Run rnprobe
|
||||
python3 -m RNS.Utilities.rnprobe example_utilities.echo.request 9382f334de63217a4278
|
||||
rnprobe example_utilities.echo.request 9382f334de63217a4278
|
||||
|
||||
# Example output
|
||||
Sent 16 byte probe to <9382f334de63217a4278>
|
||||
@@ -173,7 +294,7 @@ destinations will not have this option enabled, and will not be probable.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnprobe.py [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
||||
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
||||
|
||||
Reticulum Probe Utility
|
||||
|
||||
@@ -188,6 +309,109 @@ destinations will not have this option enabled, and will not be probable.
|
||||
-v, --verbose
|
||||
|
||||
|
||||
The rncp Utility
|
||||
================
|
||||
|
||||
The ``rncp`` utility is a simple file transfer tool. Using it, you can transfer
|
||||
files through Reticulum.
|
||||
|
||||
.. code:: text
|
||||
|
||||
# Run rncp on the receiving system, specifying which identities
|
||||
# are allowed to send files
|
||||
rncp --receive -a 940ea3f9e1037d38758f -a e28d5aee4317c24a9041
|
||||
|
||||
# From another system, copy a file to the receiving system
|
||||
rncp ~/path/to/file.tgz 256320d405d6d525d1e9
|
||||
|
||||
You can specify as many allowed senders as needed, or complete disable authentication.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rncp [-h] [--config path] [-v] [-q] [-p] [-r] [-b] [-a allowed_hash] [-n] [-w seconds] [--version] [file] [destination]
|
||||
|
||||
Reticulum File Transfer Utility
|
||||
|
||||
positional arguments:
|
||||
file file to be transferred
|
||||
destination hexadecimal hash of the receiver
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config path path to alternative Reticulum config directory
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
-r, --receive wait for incoming files
|
||||
-b, --no-announce don't announce at program start
|
||||
-a allowed_hash accept from this identity
|
||||
-n, --no-auth accept files from anyone
|
||||
-w seconds sender timeout before giving up
|
||||
--version show program's version number and exit
|
||||
-v, --verbose
|
||||
|
||||
|
||||
The rnx Utility
|
||||
================
|
||||
|
||||
The ``rnx`` utility is a basic remote command execution program. It allows you to
|
||||
execute commands on remote systems over Reticulum, and to view returned command
|
||||
output.
|
||||
|
||||
.. code:: text
|
||||
|
||||
# Run rnx on the listening system, specifying which identities
|
||||
# are allowed to execute commands
|
||||
rncp --listen -a 8111c4ff2968ab0c1286 -a 590256654482b4ba4038
|
||||
|
||||
# From another system, run a command
|
||||
rnx ad9a4c9da60089d41c29 "cat /proc/cpuinfo"
|
||||
|
||||
# Or enter the interactive mode pseudo-shell
|
||||
rnx ad9a4c9da60089d41c29 -x
|
||||
|
||||
# The default identity file is stored in
|
||||
# ~/.reticulum/identities/rnx, but you can use
|
||||
# another one, which will be created if it does
|
||||
# not already exist
|
||||
rnx ad9a4c9da60089d41c29 -i /path/to/identity
|
||||
|
||||
You can specify as many allowed senders as needed, or complete disable authentication.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-a allowed_hash] [-n] [-N] [-d] [-m] [-w seconds] [-W seconds] [--stdin STDIN] [--stdout STDOUT] [--stderr STDERR] [--version]
|
||||
[destination] [command]
|
||||
|
||||
Reticulum Remote Execution Utility
|
||||
|
||||
positional arguments:
|
||||
destination hexadecimal hash of the listener
|
||||
command command to be execute
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config path path to alternative Reticulum config directory
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
-l, --listen listen for incoming commands
|
||||
-i identity path to identity to use
|
||||
-x, --interactive enter interactive mode
|
||||
-b, --no-announce don't announce at program start
|
||||
-a allowed_hash accept from this identity
|
||||
-n, --noauth accept files from anyone
|
||||
-N, --noid don't identify to listener
|
||||
-d, --detailed show detailed result output
|
||||
-m mirror exit code of remote command
|
||||
-w seconds connect and request timeout before giving up
|
||||
-W seconds max result download time
|
||||
--stdin STDIN pass input to stdin
|
||||
--stdout STDOUT max size in bytes of returned stdout
|
||||
--stderr STDERR max size in bytes of returned stderr
|
||||
--version show program's version number and exit
|
||||
|
||||
|
||||
Improving System Configuration
|
||||
------------------------------
|
||||
|
||||
|
||||
@@ -122,6 +122,14 @@ Reticulum implements a range of generalised interface types that covers the comm
|
||||
|
||||
* UDP over IP networks
|
||||
|
||||
* Anything you can connect via stdio
|
||||
|
||||
* Reticulum can use external programs and pipes as interfaces
|
||||
|
||||
* This can be used to easily hack in virtual interfaces
|
||||
|
||||
* Or to quickly create interfaces with custom hardware
|
||||
|
||||
For a full list and more details, see the :ref:`Supported Interfaces<interfaces-main>` chapter.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.3.5 beta',
|
||||
VERSION: '0.3.7 beta',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Code Examples — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Code Examples — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<li class="right" >
|
||||
<a href="reference.html" title="API Reference"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Code Examples</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -2366,12 +2366,12 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
|
||||
<li class="right" >
|
||||
<a href="reference.html" title="API Reference"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Code Examples</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Index — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Index — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="#" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -149,6 +149,12 @@
|
||||
<h2 id="G">G</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Resource.get_data_size">get_data_size() (RNS.Resource 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.Resource.get_parts">get_parts() (RNS.Resource method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Destination.get_private_key">get_private_key() (RNS.Destination method)</a>
|
||||
|
||||
<ul>
|
||||
@@ -176,6 +182,8 @@
|
||||
<li><a href="reference.html#RNS.RequestReceipt.get_response_time">get_response_time() (RNS.RequestReceipt 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.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
|
||||
|
||||
@@ -183,6 +191,8 @@
|
||||
<li><a href="reference.html#RNS.RequestReceipt.get_status">(RNS.RequestReceipt method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#RNS.Resource.get_transfer_size">get_transfer_size() (RNS.Resource method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
@@ -207,11 +217,13 @@
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Link.identify">identify() (RNS.Link method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Identity">Identity (class in RNS)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Link.inactive_for">inactive_for() (RNS.Link method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Resource.is_compressed">is_compressed() (RNS.Resource method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
@@ -223,6 +235,8 @@
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#RNS.Link.KEEPALIVE_TIMEOUT_FACTOR">KEEPALIVE_TIMEOUT_FACTOR (RNS.Link attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Identity.KEYSIZE">KEYSIZE (RNS.Identity attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
@@ -322,6 +336,8 @@
|
||||
<li><a href="reference.html#RNS.Destination.set_default_app_data">set_default_app_data() (RNS.Destination method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.PacketReceipt.set_delivery_callback">set_delivery_callback() (RNS.PacketReceipt method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Link.set_link_closed_callback">set_link_closed_callback() (RNS.Link method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Destination.set_link_established_callback">set_link_established_callback() (RNS.Destination method)</a>
|
||||
</li>
|
||||
@@ -359,6 +375,10 @@
|
||||
<li><a href="reference.html#RNS.Identity.sign">(RNS.Identity method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#RNS.Link.STALE_GRACE">STALE_GRACE (RNS.Link attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#RNS.Link.STALE_TIME">STALE_TIME (RNS.Link attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
@@ -418,12 +438,12 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="#" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Getting Started Fast — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Getting Started Fast — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -384,12 +384,12 @@ and propose adding an interface for the hardware.</p>
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+12
-5
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Reticulum Network Stack Manual — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Reticulum Network Stack Manual — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -70,11 +70,14 @@ to participate in the development of Reticulum itself.</p>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="using.html#configuration-data">Configuration & Data</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="using.html#included-utility-programs">Included Utility Programs</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnsd-utility">The rnsd Utility</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnstatus-utility">The rnstatus Utility</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnpath-utility">The rnpath Utility</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnprobe-utility">The rnprobe Utility</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rncp-utility">The rncp Utility</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="using.html#the-rnx-utility">The rnx Utility</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="using.html#improving-system-configuration">Improving System Configuration</a><ul>
|
||||
@@ -95,7 +98,6 @@ to participate in the development of Reticulum itself.</p>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a><ul>
|
||||
<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#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>
|
||||
@@ -103,8 +105,12 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#udp-interface">UDP Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#rnode-lora-interface">RNode LoRa Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#serial-interface">Serial Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#pipe-interface">Pipe Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#kiss-interface">KISS Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#ax-25-kiss-interface">AX.25 KISS Interface</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#common-interface-options">Common Interface Options</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#interface-modes">Interface Modes</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="interfaces.html#announce-rate-control">Announce Rate Control</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a><ul>
|
||||
@@ -129,6 +135,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#packet-prioritisation">Packet Prioritisation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#interface-access-codes">Interface Access Codes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#wire-format">Wire Format</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="understanding.html#announce-propagation-rules">Announce Propagation Rules</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -218,12 +225,12 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="right" >
|
||||
<a href="whatis.html" title="What is Reticulum?"
|
||||
>next</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+274
-92
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Supported Interfaces — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Supported Interfaces — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="networks.html" title="Building Networks"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Supported Interfaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -53,88 +53,6 @@ and gives example configurations for the respective interface types.</p>
|
||||
<p>For a high-level overview of how networks can be formed over different interface
|
||||
types, have a look at the <a class="reference internal" href="networks.html#networks-main"><span class="std std-ref">Building Networks</span></a> chapter of this
|
||||
manual.</p>
|
||||
<div class="section" id="common-interface-options">
|
||||
<span id="interfaces-options"></span><h2>Common Interface Options<a class="headerlink" href="#common-interface-options" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.</p>
|
||||
<blockquote>
|
||||
<div><ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">enabled</span></code> option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to <code class="docutils literal notranslate"><span class="pre">False</span></code>. For any
|
||||
interface to be brought up, the <code class="docutils literal notranslate"><span class="pre">enabled</span></code> option
|
||||
must be set to <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">Yes</span></code>.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">mode</span></code> option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><ul class="simple">
|
||||
<li><p>The default value is <code class="docutils literal notranslate"><span class="pre">full</span></code>. In this mode, all discovery,
|
||||
meshing and transport functionality is available.</p></li>
|
||||
<li><p>In the <code class="docutils literal notranslate"><span class="pre">access_point</span></code> (or shorthand <code class="docutils literal notranslate"><span class="pre">ap</span></code>) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.</p></li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">outgoing</span></code> option sets whether an interface is allowed
|
||||
to transmit. Defaults to <code class="docutils literal notranslate"><span class="pre">True</span></code>. If set to <code class="docutils literal notranslate"><span class="pre">False</span></code> or <code class="docutils literal notranslate"><span class="pre">No</span></code>
|
||||
the interface will only receive data, and never transmit.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">network_name</span></code> option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">passphrase</span></code> option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
<code class="docutils literal notranslate"><span class="pre">network_name</span></code> option, or be used alone.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ifac_size</span></code> option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">announce_cap</span></code> option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between <code class="docutils literal notranslate"><span class="pre">1</span></code> and <code class="docutils literal notranslate"><span class="pre">100</span></code>.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">bitrate</span></code> option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn’t report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the <code class="docutils literal notranslate"><span class="pre">bitrate</span></code>
|
||||
option, to set the interface speed in <em>bits per second</em>.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</div>
|
||||
<div class="section" id="auto-interface">
|
||||
<span id="interfaces-auto"></span><h2>Auto Interface<a class="headerlink" href="#auto-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The Auto Interface enables communication with other discoverable Reticulum
|
||||
@@ -203,8 +121,8 @@ at.</p>
|
||||
<p>To use the I2P interface, you must have an I2P router running
|
||||
on your system. The easiest way to acheive this is to download and
|
||||
install the <a class="reference external" href="https://github.com/PurpleI2P/i2pd/releases/latest">latest release</a>
|
||||
of the <code class="docutils literal notranslate"><span class="pre">ì2pd</span></code> package. For more details about I2P, see the
|
||||
<a class="reference external" href="https://geti2p.net/en/about/intro">geti2p.net website</a>.`</p>
|
||||
of the <code class="docutils literal notranslate"><span class="pre">i2pd</span></code> package. For more details about I2P, see the
|
||||
<a class="reference external" href="https://geti2p.net/en/about/intro">geti2p.net website</a>.</p>
|
||||
<p>When an I2P router is running on your system, you can simply add
|
||||
an I2P interface to reticulum:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">I2P</span><span class="p">]]</span>
|
||||
@@ -282,12 +200,17 @@ you must use the i2p_tunneled option:</p>
|
||||
<span class="n">i2p_tunneled</span> <span class="o">=</span> <span class="n">yes</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In almost all cases, it is easier to use the dedicated <code class="docutils literal notranslate"><span class="pre">I2PInterface</span></code>, but for complete
|
||||
control, and using I2P routers running on external systems, this option also exists.</p>
|
||||
</div>
|
||||
<div class="section" id="tcp-client-interface">
|
||||
<span id="interfaces-tcpc"></span><h2>TCP Client Interface<a class="headerlink" href="#tcp-client-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.</p>
|
||||
<p>The TCP interface types can also tolerate intermittency in the IP link layer.
|
||||
This means that Reticulum will gracefully handle IP links that go up and down,
|
||||
and restore connectivity after a failure, once the other end of a TCP interface reappears.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here's an example of a TCP Client interface. The</span>
|
||||
<span class="c1"># target_host can either be an IP address or a hostname.</span>
|
||||
|
||||
@@ -338,15 +261,15 @@ with all other peers on a local area network.</p>
|
||||
<p><em>Please Note!</em> Using broadcast UDP traffic has performance implications,
|
||||
especially on WiFi. If your goal is simply to enable easy communication
|
||||
with all peers in your local ethernet broadcast domain, the
|
||||
<a class="reference internal" href="#interfaces-auto"><span class="std std-ref">Auto Interface</span></a> performs better, and is just as
|
||||
easy to use.</p>
|
||||
<a class="reference internal" href="#interfaces-auto"><span class="std std-ref">Auto Interface</span></a> performs better, and is even
|
||||
easier to use.</p>
|
||||
<p>The below example is enabled by default on new Reticulum installations,
|
||||
as it provides an easy way to get started and to test Reticulum on a
|
||||
pre-existing LAN.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This example enables communication with other</span>
|
||||
<span class="c1"># local Reticulum peers over UDP.</span>
|
||||
|
||||
<span class="p">[[</span><span class="n">Default</span> <span class="n">UDP</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="p">[[</span><span class="n">UDP</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">UDPInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
@@ -459,6 +382,26 @@ directly over a wire-pair, or for using devices such as data radios and lasers.<
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pipe-interface">
|
||||
<span id="interfaces-pipe"></span><h2>Pipe Interface<a class="headerlink" href="#pipe-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Using this interface, reticulum can use any program as an interface via <cite>stdin</cite> and
|
||||
<cite>stdout</cite>. This can be used to easily create virtual interfaces, or to interface with
|
||||
custom hardware or other systems.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[</span><span class="n">Pipe</span> <span class="n">Interface</span><span class="p">]]</span>
|
||||
<span class="nb">type</span> <span class="o">=</span> <span class="n">PipeInterface</span>
|
||||
<span class="n">interface_enabled</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># External command to execute</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="n">netcat</span> <span class="o">-</span><span class="n">l</span> <span class="mi">5757</span>
|
||||
|
||||
<span class="c1"># Optional respawn delay, in seconds</span>
|
||||
<span class="n">respawn_delay</span> <span class="o">=</span> <span class="mi">5</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Reticulum will write all packets to <cite>stdin</cite> of the <code class="docutils literal notranslate"><span class="pre">command</span></code> option, and will
|
||||
continously read and scan its <cite>stdout</cite> for Reticulum packets. If <code class="docutils literal notranslate"><span class="pre">EOF</span></code> is reached,
|
||||
Reticulum will try to respawn the program after waiting for <code class="docutils literal notranslate"><span class="pre">respawn_interval</span></code> seconds.</p>
|
||||
</div>
|
||||
<div class="section" id="kiss-interface">
|
||||
<span id="interfaces-kiss"></span><h2>KISS Interface<a class="headerlink" href="#kiss-interface" title="Permalink to this headline">¶</a></h2>
|
||||
<p>With the KISS interface, you can use Reticulum over a variety of packet
|
||||
@@ -567,6 +510,242 @@ beaconing functionality described above.</p>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="common-interface-options">
|
||||
<span id="interfaces-options"></span><h2>Common Interface Options<a class="headerlink" href="#common-interface-options" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.</p>
|
||||
<blockquote>
|
||||
<div><ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">enabled</span></code> option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to <code class="docutils literal notranslate"><span class="pre">False</span></code>. For any
|
||||
interface to be brought up, the <code class="docutils literal notranslate"><span class="pre">enabled</span></code> option
|
||||
must be set to <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">Yes</span></code>.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">mode</span></code> option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><ul class="simple">
|
||||
<li><p>The default value is <code class="docutils literal notranslate"><span class="pre">full</span></code>. In this mode, all discovery,
|
||||
meshing and transport functionality is available.</p></li>
|
||||
<li><p>In the <code class="docutils literal notranslate"><span class="pre">access_point</span></code> (or shorthand <code class="docutils literal notranslate"><span class="pre">ap</span></code>) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.</p></li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">outgoing</span></code> option sets whether an interface is allowed
|
||||
to transmit. Defaults to <code class="docutils literal notranslate"><span class="pre">True</span></code>. If set to <code class="docutils literal notranslate"><span class="pre">False</span></code> or <code class="docutils literal notranslate"><span class="pre">No</span></code>
|
||||
the interface will only receive data, and never transmit.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">network_name</span></code> option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">passphrase</span></code> option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
<code class="docutils literal notranslate"><span class="pre">network_name</span></code> option, or be used alone.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">ifac_size</span></code> option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">announce_cap</span></code> option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between <code class="docutils literal notranslate"><span class="pre">1</span></code> and <code class="docutils literal notranslate"><span class="pre">100</span></code>.</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p><em>If an interface exceeds its announce cap, it will queue announces
|
||||
for later transmission. Reticulum will always prioritise propagating
|
||||
announces from nearby nodes first. This ensures that the local
|
||||
topology is prioritised, and that slow networks are not overwhelmed
|
||||
by interconnected fast networks.</em></p>
|
||||
<p><em>Destinations that are rapidly re-announcing will be down-prioritised
|
||||
further. Trying to get “first-in-line” by announce spamming will have
|
||||
the exact opposite effect: Getting moved to the back of the queue every
|
||||
time a new announce from the excessively announcing destination is received.</em></p>
|
||||
<p><em>This means that it is always beneficial to select a balanced
|
||||
announce rate, and not announce more often than is actually necesarry
|
||||
for your application to function.</em></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">bitrate</span></code> option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn’t report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the <code class="docutils literal notranslate"><span class="pre">bitrate</span></code>
|
||||
option, to set the interface speed in <em>bits per second</em>.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
</div>
|
||||
<div class="section" id="interface-modes">
|
||||
<span id="interfaces-modes"></span><h2>Interface Modes<a class="headerlink" href="#interface-modes" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The optional <code class="docutils literal notranslate"><span class="pre">mode</span></code> setting is available on all interfaces, and allows
|
||||
selecting the high-level behaviour of the interface from a number of modes.
|
||||
These modes affect how Reticulum selects paths in the network, how announces
|
||||
are propagated, how long paths are valid and how paths are discovered.</p>
|
||||
<p>Configuring modes on interfaces is <strong>not</strong> strictly necessary, but can be useful
|
||||
when building or connecting to more complex networks. If your Reticulum
|
||||
instance is not running a Transport Node, it is rarely useful to configure
|
||||
interface modes, and in such cases interfaces should generally be left in
|
||||
the default mode.</p>
|
||||
<blockquote>
|
||||
<div><ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The default mode is <code class="docutils literal notranslate"><span class="pre">full</span></code>. In this mode, all discovery,
|
||||
meshing and transport functionality is activated.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">gateway</span></code> mode (or shorthand <code class="docutils literal notranslate"><span class="pre">gw</span></code>) also has all
|
||||
discovery, meshing and transport functionality available,
|
||||
but will additionally try to discover unknown paths on
|
||||
behalf of other nodes residing on the <code class="docutils literal notranslate"><span class="pre">gateway</span></code> interface.
|
||||
If Reticulum receives a path request for an unknown
|
||||
destination, from a node on a <code class="docutils literal notranslate"><span class="pre">gateway</span></code> interface, it
|
||||
will try to discover this path via all other active interfaces,
|
||||
and forward the discovered path to the requestor if one is
|
||||
found.</div>
|
||||
</div>
|
||||
<div class="line-block">
|
||||
<div class="line">If you want to allow other nodes to widely resolve paths or connect
|
||||
to a network via an interface, it might be useful to put it in this
|
||||
mode. By creating a chain of <code class="docutils literal notranslate"><span class="pre">gateway</span></code> interfaces, other
|
||||
nodes will be able to immediately discover paths to any
|
||||
destination along the chain.</div>
|
||||
</div>
|
||||
<div class="line-block">
|
||||
<div class="line"><em>Please note!</em> It is the interface <em>facing the clients</em> that
|
||||
must be put into <code class="docutils literal notranslate"><span class="pre">gateway</span></code> mode for this to work, not
|
||||
the interface facing the wider network (for this, the <code class="docutils literal notranslate"><span class="pre">boundary</span></code>
|
||||
mode can be useful, though).</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">In the <code class="docutils literal notranslate"><span class="pre">access_point</span></code> (or shorthand <code class="docutils literal notranslate"><span class="pre">ap</span></code>) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. In addition, path
|
||||
requests from clients on the access point interface will
|
||||
be handled in the same way as the <code class="docutils literal notranslate"><span class="pre">gateway</span></code> interface.</div>
|
||||
</div>
|
||||
<div class="line-block">
|
||||
<div class="line">This mode is useful for creating interfaces that remain
|
||||
quiet, until someone actually starts using them. An example
|
||||
of this could be a radio interface serving a wide area,
|
||||
where users are expected to connect momentarily, use the
|
||||
network, and then disappear again.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">roaming</span></code> mode should be used on interfaces that are
|
||||
roaming (physically mobile), seen from the perspective of
|
||||
other nodes in the network. As an example, if a vehicle is
|
||||
equipped with an external LoRa interface, and an internal,
|
||||
WiFi-based interface, that serves devices that are moving
|
||||
_with_ the vehicle, the external LoRa interface should be
|
||||
configured as <code class="docutils literal notranslate"><span class="pre">roaming</span></code>, and the internal interface can
|
||||
be left in the default mode. With transport enabled, such
|
||||
a setup will allow all internal devices to reach each other,
|
||||
and all other devices that are available on the LoRa side
|
||||
of the network, when they are in range. Devices on the LoRa
|
||||
side of the network will also be able to reach devices
|
||||
internal to the vehicle, when it is in range. Paths via
|
||||
<code class="docutils literal notranslate"><span class="pre">roaming</span></code> interfaces also expire faster.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The purpose of the <code class="docutils literal notranslate"><span class="pre">boundary</span></code> mode is to specify interfaces
|
||||
that establish connectivity with network segments that are
|
||||
significantly different than the one this node exists on.
|
||||
As an example, if a Reticulum instance is part of a LoRa-based
|
||||
network, but also has a high-speed connection to a
|
||||
public Transport Node available on the Internet, the interface
|
||||
connecting over the Internet should be set to <code class="docutils literal notranslate"><span class="pre">boundary</span></code> mode.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
<p>For a table describing the impact of all modes on announce propagation,
|
||||
please see the <a class="reference internal" href="understanding.html#understanding-announcepropagation"><span class="std std-ref">Announce Propagation Rules</span></a> section.</p>
|
||||
</div>
|
||||
<div class="section" id="announce-rate-control">
|
||||
<span id="interfaces-announcerates"></span><h2>Announce Rate Control<a class="headerlink" href="#announce-rate-control" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The built-in announce control mechanisms and the default <code class="docutils literal notranslate"><span class="pre">announce_cap</span></code>
|
||||
option described above are sufficient most of the time, but in some cases, especially on fast
|
||||
interfaces, it may be useful to control the target announce rate. Using the
|
||||
<code class="docutils literal notranslate"><span class="pre">announce_rate_target</span></code>, <code class="docutils literal notranslate"><span class="pre">announce_rate_grace</span></code> and <code class="docutils literal notranslate"><span class="pre">announce_rate_penalty</span></code>
|
||||
options, this can be done on a per-interface basis, and moderates the <em>rate at
|
||||
which received announces are re-broadcasted to other interfaces</em>.</p>
|
||||
<blockquote>
|
||||
<div><ul>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The <code class="docutils literal notranslate"><span class="pre">announce_rate_target</span></code> option sets the minimum amount of time,
|
||||
in seconds, that should pass between received announces, for any one
|
||||
destination. As an example, setting this value to <code class="docutils literal notranslate"><span class="pre">3600</span></code> means that
|
||||
announces <em>received</em> on this interface will only be re-transmitted and
|
||||
propagated to other interfaces once every hour, no matter how often they
|
||||
are received.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The optional <code class="docutils literal notranslate"><span class="pre">announce_rate_grace</span></code> defines the number of times a destination
|
||||
can violate the announce rate before the target rate is enforced.</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><div class="line-block">
|
||||
<div class="line">The optional <code class="docutils literal notranslate"><span class="pre">announce_rate_penalty</span></code> configures an extra amount of
|
||||
time that is added to the normal rate target. As an example, if a penalty
|
||||
of <code class="docutils literal notranslate"><span class="pre">7200</span></code> seconds is defined, once the rate target is enforced, the
|
||||
destination in question will only have its announces propagated every
|
||||
3 hours, until it lowers its actual announce rate to within the target.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
<p>These mechanisms, in conjunction with the <code class="docutils literal notranslate"><span class="pre">annouce_cap</span></code> mechanisms mentioned
|
||||
above means that it is essential to select a balanced announce strategy for
|
||||
your destinations. The more balanced you can make this decision, the easier
|
||||
it will be for your destinations to make it into slower networks that many hops
|
||||
away. Or you can prioritise only reaching high-capacity networks with more frequent
|
||||
announces.</p>
|
||||
<p>Current statistics and information about announce rates can be viewed using the
|
||||
<code class="docutils literal notranslate"><span class="pre">rnpath</span> <span class="pre">-r</span></code> command.</p>
|
||||
<p>It is important to note that there is no one right or wrong way to set up announce
|
||||
rates. Slower networks will naturally tend towards using less frequent announces to
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -579,7 +758,6 @@ beaconing functionality described above.</p>
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Supported Interfaces</a><ul>
|
||||
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
|
||||
<li><a class="reference internal" href="#auto-interface">Auto Interface</a></li>
|
||||
<li><a class="reference internal" href="#i2p-interface">I2P Interface</a></li>
|
||||
<li><a class="reference internal" href="#tcp-server-interface">TCP Server Interface</a></li>
|
||||
@@ -587,8 +765,12 @@ beaconing functionality described above.</p>
|
||||
<li><a class="reference internal" href="#udp-interface">UDP Interface</a></li>
|
||||
<li><a class="reference internal" href="#rnode-lora-interface">RNode LoRa Interface</a></li>
|
||||
<li><a class="reference internal" href="#serial-interface">Serial Interface</a></li>
|
||||
<li><a class="reference internal" href="#pipe-interface">Pipe Interface</a></li>
|
||||
<li><a class="reference internal" href="#kiss-interface">KISS Interface</a></li>
|
||||
<li><a class="reference internal" href="#ax-25-kiss-interface">AX.25 KISS Interface</a></li>
|
||||
<li><a class="reference internal" href="#common-interface-options">Common Interface Options</a></li>
|
||||
<li><a class="reference internal" href="#interface-modes">Interface Modes</a></li>
|
||||
<li><a class="reference internal" href="#announce-rate-control">Announce Rate Control</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -632,12 +814,12 @@ beaconing functionality described above.</p>
|
||||
<li class="right" >
|
||||
<a href="networks.html" title="Building Networks"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Supported Interfaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Building Networks — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Building Networks — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="using.html" title="Using Reticulum on Your System"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Building Networks</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -272,12 +272,12 @@ connected outliers are now an integral part of the network.</p>
|
||||
<li class="right" >
|
||||
<a href="using.html" title="Using Reticulum on Your System"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Building Networks</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
Binary file not shown.
+105
-11
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>API Reference — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>API Reference — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="understanding.html" title="Understanding Reticulum"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -484,7 +484,7 @@ relevant interfaces. Application specific data can be added to the announce.</p>
|
||||
this destination.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method to be called.</p>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(link)</em> to be called when a new link is established with this destination.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -496,7 +496,7 @@ this destination.</p>
|
||||
this destination.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method to be called.</p>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(data, packet)</em> to be called when this destination receives a packet.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -509,7 +509,7 @@ a packet sent to this destination. Allows control over when and if
|
||||
proofs should be returned for received packets.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method to be called. The callback must return one of True or False. If the callback returns True, a proof will be sent. If it returns False, a proof will not be sent.</p>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method to with the signature <em>callback(packet)</em> be called when a packet that requests a proof is received. The callback must return one of True or False. If the callback returns True, a proof will be sent. If it returns False, a proof will not be sent.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -808,7 +808,19 @@ connectivity with the specified destination.</p>
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP">
|
||||
<span class="sig-name descname"><span class="pre">ESTABLISHMENT_TIMEOUT_PER_HOP</span></span><em class="property"> <span class="pre">=</span> <span class="pre">5</span></em><a class="headerlink" href="#RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Default timeout for link establishment in seconds per hop to destination.</p>
|
||||
<dd><p>Timeout for link establishment in seconds per hop to destination.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="RNS.Link.KEEPALIVE_TIMEOUT_FACTOR">
|
||||
<span class="sig-name descname"><span class="pre">KEEPALIVE_TIMEOUT_FACTOR</span></span><em class="property"> <span class="pre">=</span> <span class="pre">4</span></em><a class="headerlink" href="#RNS.Link.KEEPALIVE_TIMEOUT_FACTOR" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>RTT timeout factor used in link timeout calculation.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="RNS.Link.STALE_GRACE">
|
||||
<span class="sig-name descname"><span class="pre">STALE_GRACE</span></span><em class="property"> <span class="pre">=</span> <span class="pre">2</span></em><a class="headerlink" href="#RNS.Link.STALE_GRACE" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Grace period in seconds used in link timeout calculation.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
@@ -817,6 +829,16 @@ connectivity with the specified destination.</p>
|
||||
<dd><p>Interval for sending keep-alive packets on established links in seconds.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="RNS.Link.STALE_TIME">
|
||||
<span class="sig-name descname"><span class="pre">STALE_TIME</span></span><em class="property"> <span class="pre">=</span> <span class="pre">720</span></em><a class="headerlink" href="#RNS.Link.STALE_TIME" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>If no traffic or keep-alive packets are received within this period, the
|
||||
link will be marked as stale, and a final keep-alive packet will be sent.
|
||||
If after this no traffic or keep-alive packets are received within <code class="docutils literal notranslate"><span class="pre">RTT</span></code> *
|
||||
<code class="docutils literal notranslate"><span class="pre">KEEPALIVE_TIMEOUT_FACTOR</span></code> + <code class="docutils literal notranslate"><span class="pre">STALE_GRACE</span></code>, the link is considered timed out,
|
||||
and will be torn down.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Link.identify">
|
||||
<span class="sig-name descname"><span class="pre">identify</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">identity</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.identify" title="Permalink to this definition">¶</a></dt>
|
||||
@@ -898,6 +920,18 @@ thus preserved. This method can be used for authentication.</p>
|
||||
be used if a new link to the same destination is established.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Link.set_link_closed_callback">
|
||||
<span class="sig-name descname"><span class="pre">set_link_closed_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_link_closed_callback" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Registers a function to be called when a link has been
|
||||
torn down.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(link)</em> to be called.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Link.set_packet_callback">
|
||||
<span class="sig-name descname"><span class="pre">set_packet_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_packet_callback" title="Permalink to this definition">¶</a></dt>
|
||||
@@ -919,7 +953,7 @@ the resource will be accepted. If it returns <em>False</em> it will
|
||||
be ignored.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(resource)</em> to be called.</p>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(resource)</em> to be called. Please note that only the basic information of the resource is available at this time, such as <em>get_transfer_size()</em>, <em>get_data_size()</em>, <em>get_parts()</em> and <em>is_compressed()</em>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -955,7 +989,7 @@ transferring over this link.</p>
|
||||
identified over this link.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(identity)</em> to be called.</p>
|
||||
<dd class="field-odd"><p><strong>callback</strong> – A function or method with the signature <em>callback(link, identity)</em> to be called.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
@@ -1081,6 +1115,66 @@ the resource advertisement it will begin transferring.</p>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Resource.get_transfer_size">
|
||||
<span class="sig-name descname"><span class="pre">get_transfer_size</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource.get_transfer_size" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="field-list simple">
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The number of bytes needed to transfer the resource.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Resource.get_data_size">
|
||||
<span class="sig-name descname"><span class="pre">get_data_size</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource.get_data_size" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="field-list simple">
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The total data size of the resource.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Resource.get_parts">
|
||||
<span class="sig-name descname"><span class="pre">get_parts</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource.get_parts" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="field-list simple">
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The number of parts the resource will be transferred in.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Resource.get_segments">
|
||||
<span class="sig-name descname"><span class="pre">get_segments</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource.get_segments" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="field-list simple">
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The number of segments the resource is divided into.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Resource.get_hash">
|
||||
<span class="sig-name descname"><span class="pre">get_hash</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource.get_hash" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="field-list simple">
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The hash of the resource.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Resource.is_compressed">
|
||||
<span class="sig-name descname"><span class="pre">is_compressed</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource.is_compressed" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="field-list simple">
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>Whether the resource is compressed.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
@@ -1173,7 +1267,7 @@ Transport system of Reticulum.</p>
|
||||
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="RNS.Transport.request_path">
|
||||
<em class="property"><span class="pre">static</span> </em><span class="sig-name descname"><span class="pre">request_path</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination_hash</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">on_interface</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Transport.request_path" title="Permalink to this definition">¶</a></dt>
|
||||
<em class="property"><span class="pre">static</span> </em><span class="sig-name descname"><span class="pre">request_path</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination_hash</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">on_interface</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">tag</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">recursive</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Transport.request_path" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Requests a path to the destination from the network. If
|
||||
another reachable peer on the network knows a path, it
|
||||
will announce it.</p>
|
||||
@@ -1258,12 +1352,12 @@ will announce it.</p>
|
||||
<li class="right" >
|
||||
<a href="understanding.html" title="Understanding Reticulum"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Search — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Search — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -85,12 +85,12 @@
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Understanding Reticulum — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Understanding Reticulum — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="interfaces.html" title="Supported Interfaces"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -80,8 +80,8 @@ LoRa radio modules with an open source firmware (see the section <a class="refer
|
||||
connected to any kind of computer or mobile device that Reticulum can run on.</p>
|
||||
<p>The ultimate aim of Reticulum is to allow anyone to be their own network operator, and to make it
|
||||
cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks.
|
||||
Reticulum <strong>is not</strong> <em>one network</em>, it <strong>is a tool</strong> to build <em>thousands of networks</em>.</p>
|
||||
<p>Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
Reticulum <strong>is not</strong> <em>one network</em>, it <strong>is a tool</strong> to build <em>thousands of networks</em>. Networks without
|
||||
kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
with each other, and require no central oversight. Networks for human beings. <em>Networks for the people</em>.</p>
|
||||
</div>
|
||||
<div class="section" id="goals">
|
||||
@@ -858,6 +858,15 @@ but excluding any interface access codes.
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="announce-propagation-rules">
|
||||
<span id="understanding-announcepropagation"></span><h3>Announce Propagation Rules<a class="headerlink" href="#announce-propagation-rules" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The following table illustrates the rules for automatically propagating announces
|
||||
from one interface type to another, for all possible combinations. For the purpose
|
||||
of announce propagation, the <em>Full</em> and <em>Gateway</em> modes are identical.</p>
|
||||
<img alt="_images/if_mode_graph_b.png" src="_images/if_mode_graph_b.png" />
|
||||
<p>See the <a class="reference internal" href="interfaces.html#interfaces-modes"><span class="std std-ref">Interface Modes</span></a> section for a conceptual overview
|
||||
of the different interface modes, and how they are configured.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -898,6 +907,7 @@ but excluding any interface access codes.
|
||||
<li><a class="reference internal" href="#packet-prioritisation">Packet Prioritisation</a></li>
|
||||
<li><a class="reference internal" href="#interface-access-codes">Interface Access Codes</a></li>
|
||||
<li><a class="reference internal" href="#wire-format">Wire Format</a></li>
|
||||
<li><a class="reference internal" href="#announce-propagation-rules">Announce Propagation Rules</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -943,12 +953,12 @@ but excluding any interface access codes.
|
||||
<li class="right" >
|
||||
<a href="interfaces.html" title="Supported Interfaces"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+228
-17
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Using Reticulum on Your System — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>Using Reticulum on Your System — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="gettingstartedfast.html" title="Getting Started Fast"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Using Reticulum on Your System</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -56,16 +56,130 @@ program starts up and also wants access to the same Reticulum network, the
|
||||
instance is simply shared. This works for any number of programs running
|
||||
concurrently, and is very easy to use, but depending on your use case, there
|
||||
are other options.</p>
|
||||
<div class="section" id="configuration-data">
|
||||
<h2>Configuration & Data<a class="headerlink" href="#configuration-data" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A Reticulum stores all information that it needs to function in a single file-
|
||||
system directory. By default, this directory is <code class="docutils literal notranslate"><span class="pre">~/.reticulum</span></code>, but you can
|
||||
use any directory you wish. You can also run multiple separate Reticulum
|
||||
instances on the same physical system, in complete isolation from each other,
|
||||
or connected together.</p>
|
||||
<p>In most cases, a single physical system will only need to run one Reticulum
|
||||
instance. This can either be launched at boot, as a system service, or simply
|
||||
be brought up when a program needs it. In either case, any number of programs
|
||||
running on the same system will automatically share the same Reticulum instance,
|
||||
if the configuration allows for it, which it does by default.</p>
|
||||
<p>The entire configuration of Reticulum is found in the <code class="docutils literal notranslate"><span class="pre">~/.reticulum/config</span></code>
|
||||
file. When Reticulum is first started on a new system, a basic, functional
|
||||
configuration file is created. The default configuration looks like this:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This is the default Reticulum config file.</span>
|
||||
<span class="c1"># You should probably edit it to include any additional,</span>
|
||||
<span class="c1"># interfaces and settings you might need.</span>
|
||||
|
||||
<span class="c1"># Only the most basic options are included in this default</span>
|
||||
<span class="c1"># configuration. To see a more verbose, and much longer,</span>
|
||||
<span class="c1"># configuration example, you can run the command:</span>
|
||||
<span class="c1"># rnsd --exampleconfig</span>
|
||||
|
||||
|
||||
<span class="p">[</span><span class="n">reticulum</span><span class="p">]</span>
|
||||
|
||||
<span class="c1"># If you enable Transport, your system will route traffic</span>
|
||||
<span class="c1"># for other peers, pass announces and serve path requests.</span>
|
||||
<span class="c1"># This should only be done for systems that are suited to</span>
|
||||
<span class="c1"># act as transport nodes, ie. if they are stationary and</span>
|
||||
<span class="c1"># always-on. This directive is optional and can be removed</span>
|
||||
<span class="c1"># for brevity.</span>
|
||||
|
||||
<span class="n">enable_transport</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
|
||||
|
||||
<span class="c1"># By default, the first program to launch the Reticulum</span>
|
||||
<span class="c1"># Network Stack will create a shared instance, that other</span>
|
||||
<span class="c1"># programs can communicate with. Only the shared instance</span>
|
||||
<span class="c1"># opens all the configured interfaces directly, and other</span>
|
||||
<span class="c1"># local programs communicate with the shared instance over</span>
|
||||
<span class="c1"># a local socket. This is completely transparent to the</span>
|
||||
<span class="c1"># user, and should generally be turned on. This directive</span>
|
||||
<span class="c1"># is optional and can be removed for brevity.</span>
|
||||
|
||||
<span class="n">share_instance</span> <span class="o">=</span> <span class="n">Yes</span>
|
||||
|
||||
|
||||
<span class="c1"># If you want to run multiple *different* shared instances</span>
|
||||
<span class="c1"># on the same system, you will need to specify different</span>
|
||||
<span class="c1"># shared instance ports for each. The defaults are given</span>
|
||||
<span class="c1"># below, and again, these options can be left out if you</span>
|
||||
<span class="c1"># don't need them.</span>
|
||||
|
||||
<span class="n">shared_instance_port</span> <span class="o">=</span> <span class="mi">37428</span>
|
||||
<span class="n">instance_control_port</span> <span class="o">=</span> <span class="mi">37429</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>
|
||||
<span class="c1"># an optional directive, and can be left out for brevity.</span>
|
||||
<span class="c1"># This behaviour is disabled by default.</span>
|
||||
|
||||
<span class="n">panic_on_interface_error</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>
|
||||
<span class="c1"># 1: Log errors and lower log levels</span>
|
||||
<span class="c1"># 2: Log warnings and lower log levels</span>
|
||||
<span class="c1"># 3: Log notices and lower log levels</span>
|
||||
<span class="c1"># 4: Log info and lower (this is the default)</span>
|
||||
<span class="c1"># 5: Verbose logging</span>
|
||||
<span class="c1"># 6: Debug logging</span>
|
||||
<span class="c1"># 7: Extreme logging</span>
|
||||
|
||||
<span class="n">loglevel</span> <span class="o">=</span> <span class="mi">4</span>
|
||||
|
||||
|
||||
<span class="c1"># The interfaces section defines the physical and virtual</span>
|
||||
<span class="c1"># interfaces Reticulum will use to communicate on. This</span>
|
||||
<span class="c1"># section will contain examples for a variety of interface</span>
|
||||
<span class="c1"># types. You can modify these or use them as a basis for</span>
|
||||
<span class="c1"># your own config, or simply remove the unused ones.</span>
|
||||
|
||||
<span class="p">[</span><span class="n">interfaces</span><span class="p">]</span>
|
||||
|
||||
<span class="c1"># This interface enables communication with other</span>
|
||||
<span class="c1"># link-local Reticulum nodes over UDP. It does not</span>
|
||||
<span class="c1"># need any functional IP infrastructure like routers</span>
|
||||
<span class="c1"># or DHCP servers, but will require that at least link-</span>
|
||||
<span class="c1"># local IPv6 is enabled in your operating system, which</span>
|
||||
<span class="c1"># should be enabled by default in almost any OS. See</span>
|
||||
<span class="c1"># the Reticulum Manual for more configuration options.</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>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If Reticulum infrastructure already exists locally, you probably don’t need to
|
||||
change anything, and you may already be connected to a wider network. If not,
|
||||
you will probably need to add relevant <em>interfaces</em> to the configuration, in
|
||||
order to communicate with other systems. It is a good idea to read the comments
|
||||
and explanations in the above default config. It will teach you the basic
|
||||
concepts you need to understand to configure your network. Once you have done that,
|
||||
take a look at the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Interfaces</span></a> chapter of this manual.</p>
|
||||
</div>
|
||||
<div class="section" id="included-utility-programs">
|
||||
<h2>Included Utility Programs<a class="headerlink" href="#included-utility-programs" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Reticulum includes a range of useful utilities, both for managing your Reticulum
|
||||
networks, and for carrying out common tasks over Reticulum networks, such as
|
||||
transferring files to remote systems, and executing commands and programs remotely.</p>
|
||||
<p>If you often use Reticulum from several different programs, or simply want
|
||||
Reticulum to stay available all the time, for example if you are hosting
|
||||
a transport node, you might want to run Reticulum as a separate service that
|
||||
other programs, applications and services can utilise.</p>
|
||||
<div class="section" id="the-rnsd-utility">
|
||||
<h3>The rnsd Utility<a class="headerlink" href="#the-rnsd-utility" title="Permalink to this headline">¶</a></h3>
|
||||
<p>To do so is very easy. Simply run the included <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> command. When <code class="docutils literal notranslate"><span class="pre">rnsd</span></code>
|
||||
is running, it will keep all configured interfaces open, handle transport if
|
||||
<p>It is very easy to run Reticulum as a service. Simply run the included <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> command.
|
||||
When <code class="docutils literal notranslate"><span class="pre">rnsd</span></code> is running, it will keep all configured interfaces open, handle transport if
|
||||
it is enabled, and allow any other programs to immediately utilise the
|
||||
Reticulum network it is configured for.</p>
|
||||
<p>You can even run multiple instances of rnsd with different configurations on
|
||||
@@ -156,21 +270,22 @@ rnpath eca6f4e4dc26ae329e61
|
||||
Path found, destination <eca6f4e4dc26ae329e61> is 4 hops away via <56b115c30cd386cad69c> on TCPInterface[Testnet/frankfurt.rns.unsigned.io:4965]
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-d] [-w seconds] [-v]
|
||||
[destination]
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D] [-w seconds] [-v] [destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
positional arguments:
|
||||
destination hexadecimal hash of the destination
|
||||
destination hexadecimal hash of the destination
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-d, --drop remove the path to a destination
|
||||
-w seconds timeout before giving up
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-r, --rates show announce rate info
|
||||
-d, --drop remove the path to a destination
|
||||
-D, --drop-announces drop all queued announces
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
</pre></div>
|
||||
</div>
|
||||
@@ -182,7 +297,7 @@ to the <code class="docutils literal notranslate"><span class="pre">ping</span><
|
||||
specified destination is configured to send proofs for received packets. Many
|
||||
destinations will not have this option enabled, and will not be probable.</p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnprobe
|
||||
python3 -m RNS.Utilities.rnprobe example_utilities.echo.request 9382f334de63217a4278
|
||||
rnprobe example_utilities.echo.request 9382f334de63217a4278
|
||||
|
||||
# Example output
|
||||
Sent 16 byte probe to <9382f334de63217a4278>
|
||||
@@ -190,7 +305,7 @@ Valid reply received from <9382f334de63217a4278>
|
||||
Round-trip time is 38.469 milliseconds over 2 hops
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnprobe.py [-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] [full_name] [destination_hash]
|
||||
|
||||
Reticulum Probe Utility
|
||||
|
||||
@@ -206,6 +321,99 @@ optional arguments:
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="the-rncp-utility">
|
||||
<h3>The rncp Utility<a class="headerlink" href="#the-rncp-utility" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">rncp</span></code> utility is a simple file transfer tool. Using it, you can transfer
|
||||
files through Reticulum.</p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rncp on the receiving system, specifying which identities
|
||||
# are allowed to send files
|
||||
rncp --receive -a 940ea3f9e1037d38758f -a e28d5aee4317c24a9041
|
||||
|
||||
# From another system, copy a file to the receiving system
|
||||
rncp ~/path/to/file.tgz 256320d405d6d525d1e9
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can specify as many allowed senders as needed, or complete disable authentication.</p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rncp [-h] [--config path] [-v] [-q] [-p] [-r] [-b] [-a allowed_hash] [-n] [-w seconds] [--version] [file] [destination]
|
||||
|
||||
Reticulum File Transfer Utility
|
||||
|
||||
positional arguments:
|
||||
file file to be transferred
|
||||
destination hexadecimal hash of the receiver
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config path path to alternative Reticulum config directory
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
-r, --receive wait for incoming files
|
||||
-b, --no-announce don't announce at program start
|
||||
-a allowed_hash accept from this identity
|
||||
-n, --no-auth accept files from anyone
|
||||
-w seconds sender timeout before giving up
|
||||
--version show program's version number and exit
|
||||
-v, --verbose
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="the-rnx-utility">
|
||||
<h3>The rnx Utility<a class="headerlink" href="#the-rnx-utility" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">rnx</span></code> utility is a basic remote command execution program. It allows you to
|
||||
execute commands on remote systems over Reticulum, and to view returned command
|
||||
output.</p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnx on the listening system, specifying which identities
|
||||
# are allowed to execute commands
|
||||
rncp --listen -a 8111c4ff2968ab0c1286 -a 590256654482b4ba4038
|
||||
|
||||
# From another system, run a command
|
||||
rnx ad9a4c9da60089d41c29 "cat /proc/cpuinfo"
|
||||
|
||||
# Or enter the interactive mode pseudo-shell
|
||||
rnx ad9a4c9da60089d41c29 -x
|
||||
|
||||
# The default identity file is stored in
|
||||
# ~/.reticulum/identities/rnx, but you can use
|
||||
# another one, which will be created if it does
|
||||
# not already exist
|
||||
rnx ad9a4c9da60089d41c29 -i /path/to/identity
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can specify as many allowed senders as needed, or complete disable authentication.</p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-a allowed_hash] [-n] [-N] [-d] [-m] [-w seconds] [-W seconds] [--stdin STDIN] [--stdout STDOUT] [--stderr STDERR] [--version]
|
||||
[destination] [command]
|
||||
|
||||
Reticulum Remote Execution Utility
|
||||
|
||||
positional arguments:
|
||||
destination hexadecimal hash of the listener
|
||||
command command to be execute
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config path path to alternative Reticulum config directory
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
-l, --listen listen for incoming commands
|
||||
-i identity path to identity to use
|
||||
-x, --interactive enter interactive mode
|
||||
-b, --no-announce don't announce at program start
|
||||
-a allowed_hash accept from this identity
|
||||
-n, --noauth accept files from anyone
|
||||
-N, --noid don't identify to listener
|
||||
-d, --detailed show detailed result output
|
||||
-m mirror exit code of remote command
|
||||
-w seconds connect and request timeout before giving up
|
||||
-W seconds max result download time
|
||||
--stdin STDIN pass input to stdin
|
||||
--stdout STDOUT max size in bytes of returned stdout
|
||||
--stderr STDERR max size in bytes of returned stderr
|
||||
--version show program's version number and exit
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="improving-system-configuration">
|
||||
<h2>Improving System Configuration<a class="headerlink" href="#improving-system-configuration" title="Permalink to this headline">¶</a></h2>
|
||||
@@ -297,11 +505,14 @@ WantedBy=multi-user.target
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Using Reticulum on Your System</a><ul>
|
||||
<li><a class="reference internal" href="#configuration-data">Configuration & Data</a></li>
|
||||
<li><a class="reference internal" href="#included-utility-programs">Included Utility Programs</a><ul>
|
||||
<li><a class="reference internal" href="#the-rnsd-utility">The rnsd Utility</a></li>
|
||||
<li><a class="reference internal" href="#the-rnstatus-utility">The rnstatus Utility</a></li>
|
||||
<li><a class="reference internal" href="#the-rnpath-utility">The rnpath Utility</a></li>
|
||||
<li><a class="reference internal" href="#the-rnprobe-utility">The rnprobe Utility</a></li>
|
||||
<li><a class="reference internal" href="#the-rncp-utility">The rncp Utility</a></li>
|
||||
<li><a class="reference internal" href="#the-rnx-utility">The rnx Utility</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#improving-system-configuration">Improving System Configuration</a><ul>
|
||||
@@ -352,12 +563,12 @@ WantedBy=multi-user.target
|
||||
<li class="right" >
|
||||
<a href="gettingstartedfast.html" title="Getting Started Fast"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Using Reticulum on Your System</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+11
-4
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>What is Reticulum? — Reticulum Network Stack 0.3.5 beta documentation</title>
|
||||
<title>What is Reticulum? — Reticulum Network Stack 0.3.7 beta documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Reticulum Network Stack Manual"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -137,6 +137,13 @@ network, and vice versa.</p>
|
||||
<li><p>The I2P network</p></li>
|
||||
<li><p>TCP over IP networks</p></li>
|
||||
<li><p>UDP over IP networks</p></li>
|
||||
<li><p>Anything you can connect via stdio</p>
|
||||
<ul>
|
||||
<li><p>Reticulum can use external programs and pipes as interfaces</p></li>
|
||||
<li><p>This can be used to easily hack in virtual interfaces</p></li>
|
||||
<li><p>Or to quickly create interfaces with custom hardware</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>For a full list and more details, see the <a class="reference internal" href="interfaces.html#interfaces-main"><span class="std std-ref">Supported Interfaces</span></a> chapter.</p>
|
||||
</div>
|
||||
@@ -204,12 +211,12 @@ network, and vice versa.</p>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Reticulum Network Stack Manual"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.5 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.3.7 beta documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2021, Mark Qvist.
|
||||
© Copyright 2022, Mark Qvist.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 4.0.1.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+2
-2
@@ -18,11 +18,11 @@ sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Reticulum Network Stack'
|
||||
copyright = '2021, Mark Qvist'
|
||||
copyright = '2022, Mark Qvist'
|
||||
author = 'Mark Qvist'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.3.5 beta'
|
||||
release = '0.3.7 beta'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
+242
-70
@@ -19,71 +19,6 @@ types, have a look at the :ref:`Building Networks<networks-main>` chapter of thi
|
||||
manual.
|
||||
|
||||
|
||||
.. _interfaces-options:
|
||||
|
||||
Common Interface Options
|
||||
========================
|
||||
|
||||
A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.
|
||||
|
||||
|
||||
* | The ``enabled`` option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to ``False``. For any
|
||||
interface to be brought up, the ``enabled`` option
|
||||
must be set to ``True`` or ``Yes``.
|
||||
|
||||
* | The ``mode`` option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.
|
||||
|
||||
- The default value is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is available.
|
||||
|
||||
- In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.
|
||||
|
||||
* | The ``outgoing`` option sets whether an interface is allowed
|
||||
to transmit. Defaults to ``True``. If set to ``False`` or ``No``
|
||||
the interface will only receive data, and never transmit.
|
||||
|
||||
* | The ``network_name`` option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.
|
||||
|
||||
* | The ``passphrase`` option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
``network_name`` option, or be used alone.
|
||||
|
||||
* | The ``ifac_size`` option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.
|
||||
|
||||
* | The ``announce_cap`` option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between ``1`` and ``100``.
|
||||
|
||||
* | The ``bitrate`` option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn't report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the ``bitrate``
|
||||
option, to set the interface speed in *bits per second*.
|
||||
|
||||
|
||||
.. _interfaces-auto:
|
||||
|
||||
Auto Interface
|
||||
@@ -165,8 +100,8 @@ at.
|
||||
To use the I2P interface, you must have an I2P router running
|
||||
on your system. The easiest way to acheive this is to download and
|
||||
install the `latest release <https://github.com/PurpleI2P/i2pd/releases/latest>`_
|
||||
of the ``ì2pd`` package. For more details about I2P, see the
|
||||
`geti2p.net website <https://geti2p.net/en/about/intro>`_.`
|
||||
of the ``i2pd`` package. For more details about I2P, see the
|
||||
`geti2p.net website <https://geti2p.net/en/about/intro>`_.
|
||||
|
||||
When an I2P router is running on your system, you can simply add
|
||||
an I2P interface to reticulum:
|
||||
@@ -260,6 +195,9 @@ you must use the i2p_tunneled option:
|
||||
listen_port = 5001
|
||||
i2p_tunneled = yes
|
||||
|
||||
In almost all cases, it is easier to use the dedicated ``I2PInterface``, but for complete
|
||||
control, and using I2P routers running on external systems, this option also exists.
|
||||
|
||||
.. _interfaces-tcpc:
|
||||
|
||||
TCP Client Interface
|
||||
@@ -269,6 +207,10 @@ To connect to a TCP server interface, you would naturally use the TCP client
|
||||
interface. Many TCP Client interfaces from different peers can connect to the
|
||||
same TCP Server interface at the same time.
|
||||
|
||||
The TCP interface types can also tolerate intermittency in the IP link layer.
|
||||
This means that Reticulum will gracefully handle IP links that go up and down,
|
||||
and restore connectivity after a failure, once the other end of a TCP interface reappears.
|
||||
|
||||
.. code::
|
||||
|
||||
# Here's an example of a TCP Client interface. The
|
||||
@@ -329,8 +271,8 @@ with all other peers on a local area network.
|
||||
*Please Note!* Using broadcast UDP traffic has performance implications,
|
||||
especially on WiFi. If your goal is simply to enable easy communication
|
||||
with all peers in your local ethernet broadcast domain, the
|
||||
:ref:`Auto Interface<interfaces-auto>` performs better, and is just as
|
||||
easy to use.
|
||||
:ref:`Auto Interface<interfaces-auto>` performs better, and is even
|
||||
easier to use.
|
||||
|
||||
The below example is enabled by default on new Reticulum installations,
|
||||
as it provides an easy way to get started and to test Reticulum on a
|
||||
@@ -341,7 +283,7 @@ pre-existing LAN.
|
||||
# This example enables communication with other
|
||||
# local Reticulum peers over UDP.
|
||||
|
||||
[[Default UDP Interface]]
|
||||
[[UDP Interface]]
|
||||
type = UDPInterface
|
||||
interface_enabled = True
|
||||
|
||||
@@ -461,6 +403,31 @@ directly over a wire-pair, or for using devices such as data radios and lasers.
|
||||
parity = none
|
||||
stopbits = 1
|
||||
|
||||
.. _interfaces-pipe:
|
||||
|
||||
Pipe Interface
|
||||
==============
|
||||
|
||||
Using this interface, reticulum can use any program as an interface via `stdin` and
|
||||
`stdout`. This can be used to easily create virtual interfaces, or to interface with
|
||||
custom hardware or other systems.
|
||||
|
||||
.. code::
|
||||
|
||||
[[Pipe Interface]]
|
||||
type = PipeInterface
|
||||
interface_enabled = True
|
||||
|
||||
# External command to execute
|
||||
command = netcat -l 5757
|
||||
|
||||
# Optional respawn delay, in seconds
|
||||
respawn_delay = 5
|
||||
|
||||
Reticulum will write all packets to `stdin` of the ``command`` option, and will
|
||||
continously read and scan its `stdout` for Reticulum packets. If ``EOF`` is reached,
|
||||
Reticulum will try to respawn the program after waiting for ``respawn_interval`` seconds.
|
||||
|
||||
.. _interfaces-kiss:
|
||||
|
||||
KISS Interface
|
||||
@@ -578,3 +545,208 @@ beaconing functionality described above.
|
||||
# This is useful for modems with a
|
||||
# small internal packet buffer.
|
||||
flow_control = false
|
||||
|
||||
.. _interfaces-options:
|
||||
|
||||
Common Interface Options
|
||||
========================
|
||||
|
||||
A number of general configuration options are available on most interfaces.
|
||||
These can be used to control various aspects of interface behaviour.
|
||||
|
||||
|
||||
* | The ``enabled`` option tells Reticulum whether or not
|
||||
to bring up the interface. Defaults to ``False``. For any
|
||||
interface to be brought up, the ``enabled`` option
|
||||
must be set to ``True`` or ``Yes``.
|
||||
|
||||
* | The ``mode`` option allows selecting the high-level behaviour
|
||||
of the interface from a number of options.
|
||||
|
||||
- The default value is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is available.
|
||||
|
||||
- In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. This mode is useful
|
||||
for creating interfaces that are mostly quiet, unless when
|
||||
someone is actually using them. An example of this could
|
||||
be a radio interface serving a wide area, where users are
|
||||
expected to connect momentarily, use the network, and then
|
||||
disappear again.
|
||||
|
||||
* | The ``outgoing`` option sets whether an interface is allowed
|
||||
to transmit. Defaults to ``True``. If set to ``False`` or ``No``
|
||||
the interface will only receive data, and never transmit.
|
||||
|
||||
* | The ``network_name`` option sets the virtual network name for
|
||||
the interface. This allows multiple separate network segments
|
||||
to exist on the same physical channel or medium.
|
||||
|
||||
* | The ``passphrase`` option sets an authentication passphrase on
|
||||
the interface. This option can be used in conjunction with the
|
||||
``network_name`` option, or be used alone.
|
||||
|
||||
* | The ``ifac_size`` option allows customising the length of the
|
||||
Interface Authentication Codes carried by each packet on named
|
||||
and/or authenticated network segments. It is set by default to
|
||||
a size suitable for the interface in question, but can be set
|
||||
to a custom size between 8 and 512 bits by using this option.
|
||||
In normal usage, this option should not be changed from the
|
||||
default.
|
||||
|
||||
* | The ``announce_cap`` option lets you configure the maximum
|
||||
bandwidth to allocate, at any given time, to propagating
|
||||
announces and other network upkeep traffic. It is configured at
|
||||
2% by default, and should normally not need to be changed. Can
|
||||
be set to any value between ``1`` and ``100``.
|
||||
|
||||
*If an interface exceeds its announce cap, it will queue announces
|
||||
for later transmission. Reticulum will always prioritise propagating
|
||||
announces from nearby nodes first. This ensures that the local
|
||||
topology is prioritised, and that slow networks are not overwhelmed
|
||||
by interconnected fast networks.*
|
||||
|
||||
*Destinations that are rapidly re-announcing will be down-prioritised
|
||||
further. Trying to get "first-in-line" by announce spamming will have
|
||||
the exact opposite effect: Getting moved to the back of the queue every
|
||||
time a new announce from the excessively announcing destination is received.*
|
||||
|
||||
*This means that it is always beneficial to select a balanced
|
||||
announce rate, and not announce more often than is actually necesarry
|
||||
for your application to function.*
|
||||
|
||||
* | The ``bitrate`` option configures the interface bitrate.
|
||||
Reticulum will use interface speeds reported by hardware, or
|
||||
try to guess a suitable rate when the hardware doesn't report
|
||||
any. In most cases, the automatically found rate should be
|
||||
sufficient, but it can be configured by using the ``bitrate``
|
||||
option, to set the interface speed in *bits per second*.
|
||||
|
||||
|
||||
.. _interfaces-modes:
|
||||
|
||||
Interface Modes
|
||||
===============
|
||||
|
||||
The optional ``mode`` setting is available on all interfaces, and allows
|
||||
selecting the high-level behaviour of the interface from a number of modes.
|
||||
These modes affect how Reticulum selects paths in the network, how announces
|
||||
are propagated, how long paths are valid and how paths are discovered.
|
||||
|
||||
Configuring modes on interfaces is **not** strictly necessary, but can be useful
|
||||
when building or connecting to more complex networks. If your Reticulum
|
||||
instance is not running a Transport Node, it is rarely useful to configure
|
||||
interface modes, and in such cases interfaces should generally be left in
|
||||
the default mode.
|
||||
|
||||
* | The default mode is ``full``. In this mode, all discovery,
|
||||
meshing and transport functionality is activated.
|
||||
|
||||
* | The ``gateway`` mode (or shorthand ``gw``) also has all
|
||||
discovery, meshing and transport functionality available,
|
||||
but will additionally try to discover unknown paths on
|
||||
behalf of other nodes residing on the ``gateway`` interface.
|
||||
If Reticulum receives a path request for an unknown
|
||||
destination, from a node on a ``gateway`` interface, it
|
||||
will try to discover this path via all other active interfaces,
|
||||
and forward the discovered path to the requestor if one is
|
||||
found.
|
||||
|
||||
| If you want to allow other nodes to widely resolve paths or connect
|
||||
to a network via an interface, it might be useful to put it in this
|
||||
mode. By creating a chain of ``gateway`` interfaces, other
|
||||
nodes will be able to immediately discover paths to any
|
||||
destination along the chain.
|
||||
|
||||
| *Please note!* It is the interface *facing the clients* that
|
||||
must be put into ``gateway`` mode for this to work, not
|
||||
the interface facing the wider network (for this, the ``boundary``
|
||||
mode can be useful, though).
|
||||
|
||||
* | In the ``access_point`` (or shorthand ``ap``) mode, the
|
||||
interface will operate as a network access point. In this
|
||||
mode, announces will not be automatically broadcasted on
|
||||
the interface, and paths to destinations on the interface
|
||||
will have a much shorter expiry time. In addition, path
|
||||
requests from clients on the access point interface will
|
||||
be handled in the same way as the ``gateway`` interface.
|
||||
|
||||
| This mode is useful for creating interfaces that remain
|
||||
quiet, until someone actually starts using them. An example
|
||||
of this could be a radio interface serving a wide area,
|
||||
where users are expected to connect momentarily, use the
|
||||
network, and then disappear again.
|
||||
|
||||
* | The ``roaming`` mode should be used on interfaces that are
|
||||
roaming (physically mobile), seen from the perspective of
|
||||
other nodes in the network. As an example, if a vehicle is
|
||||
equipped with an external LoRa interface, and an internal,
|
||||
WiFi-based interface, that serves devices that are moving
|
||||
_with_ the vehicle, the external LoRa interface should be
|
||||
configured as ``roaming``, and the internal interface can
|
||||
be left in the default mode. With transport enabled, such
|
||||
a setup will allow all internal devices to reach each other,
|
||||
and all other devices that are available on the LoRa side
|
||||
of the network, when they are in range. Devices on the LoRa
|
||||
side of the network will also be able to reach devices
|
||||
internal to the vehicle, when it is in range. Paths via
|
||||
``roaming`` interfaces also expire faster.
|
||||
|
||||
* | The purpose of the ``boundary`` mode is to specify interfaces
|
||||
that establish connectivity with network segments that are
|
||||
significantly different than the one this node exists on.
|
||||
As an example, if a Reticulum instance is part of a LoRa-based
|
||||
network, but also has a high-speed connection to a
|
||||
public Transport Node available on the Internet, the interface
|
||||
connecting over the Internet should be set to ``boundary`` mode.
|
||||
|
||||
For a table describing the impact of all modes on announce propagation,
|
||||
please see the :ref:`Announce Propagation Rules<understanding-announcepropagation>` section.
|
||||
|
||||
.. _interfaces-announcerates:
|
||||
|
||||
Announce Rate Control
|
||||
=====================
|
||||
|
||||
The built-in announce control mechanisms and the default ``announce_cap``
|
||||
option described above are sufficient most of the time, but in some cases, especially on fast
|
||||
interfaces, it may be useful to control the target announce rate. Using the
|
||||
``announce_rate_target``, ``announce_rate_grace`` and ``announce_rate_penalty``
|
||||
options, this can be done on a per-interface basis, and moderates the *rate at
|
||||
which received announces are re-broadcasted to other interfaces*.
|
||||
|
||||
* | The ``announce_rate_target`` option sets the minimum amount of time,
|
||||
in seconds, that should pass between received announces, for any one
|
||||
destination. As an example, setting this value to ``3600`` means that
|
||||
announces *received* on this interface will only be re-transmitted and
|
||||
propagated to other interfaces once every hour, no matter how often they
|
||||
are received.
|
||||
|
||||
* | The optional ``announce_rate_grace`` defines the number of times a destination
|
||||
can violate the announce rate before the target rate is enforced.
|
||||
|
||||
* | The optional ``announce_rate_penalty`` configures an extra amount of
|
||||
time that is added to the normal rate target. As an example, if a penalty
|
||||
of ``7200`` seconds is defined, once the rate target is enforced, the
|
||||
destination in question will only have its announces propagated every
|
||||
3 hours, until it lowers its actual announce rate to within the target.
|
||||
|
||||
These mechanisms, in conjunction with the ``annouce_cap`` mechanisms mentioned
|
||||
above means that it is essential to select a balanced announce strategy for
|
||||
your destinations. The more balanced you can make this decision, the easier
|
||||
it will be for your destinations to make it into slower networks that many hops
|
||||
away. Or you can prioritise only reaching high-capacity networks with more frequent
|
||||
announces.
|
||||
|
||||
Current statistics and information about announce rates can be viewed using the
|
||||
``rnpath -r`` command.
|
||||
|
||||
It is important to note that there is no one right or wrong way to set up announce
|
||||
rates. Slower networks will naturally tend towards using less frequent announces to
|
||||
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.
|
||||
|
||||
|
||||
@@ -51,9 +51,8 @@ connected to any kind of computer or mobile device that Reticulum can run on.
|
||||
|
||||
The ultimate aim of Reticulum is to allow anyone to be their own network operator, and to make it
|
||||
cheap and easy to cover vast areas with a myriad of independent, interconnectable and autonomous networks.
|
||||
Reticulum **is not** *one network*, it **is a tool** to build *thousands of networks*.
|
||||
|
||||
Networks without kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
Reticulum **is not** *one network*, it **is a tool** to build *thousands of networks*. Networks without
|
||||
kill-switches, surveillance, censorship and control. Networks that can freely interoperate, associate and disassociate
|
||||
with each other, and require no central oversight. Networks for human beings. *Networks for the people*.
|
||||
|
||||
.. _understanding-goals:
|
||||
@@ -802,3 +801,41 @@ Wire Format
|
||||
- Link Proof : 77 bytes
|
||||
- Link RTT packet : 83 bytes
|
||||
- Link keepalive : 14 bytes
|
||||
|
||||
|
||||
.. _understanding-announcepropagation:
|
||||
|
||||
Announce Propagation Rules
|
||||
--------------------------
|
||||
|
||||
The following table illustrates the rules for automatically propagating announces
|
||||
from one interface type to another, for all possible combinations. For the purpose
|
||||
of announce propagation, the *Full* and *Gateway* modes are identical.
|
||||
|
||||
.. image:: graphics/if_mode_graph_b.png
|
||||
|
||||
See the :ref:`Interface Modes<interfaces-modes>` section for a conceptual overview
|
||||
of the different interface modes, and how they are configured.
|
||||
|
||||
..
|
||||
(.. code-block:: text)
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼───> Full >───┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✓ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✕ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✕ ──┼────> AP >────┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✓ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Roaming >──┼── ✕ ── AP
|
||||
Boundary ── ✕ ──┤ ├── ✕ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
Full ────── ✓ ──┐ ┌── ✓ ── Full
|
||||
AP ──────── ✓ ──┼─> Boundary >─┼── ✕ ── AP
|
||||
Boundary ── ✓ ──┤ ├── ✓ ── Boundary
|
||||
Roaming ─── ✕ ──┘ └── ✕ ── Roaming
|
||||
|
||||
|
||||
+240
-16
@@ -19,9 +19,129 @@ instance is simply shared. This works for any number of programs running
|
||||
concurrently, and is very easy to use, but depending on your use case, there
|
||||
are other options.
|
||||
|
||||
Configuration & Data
|
||||
--------------------
|
||||
|
||||
A Reticulum stores all information that it needs to function in a single file-
|
||||
system directory. By default, this directory is ``~/.reticulum``, but you can
|
||||
use any directory you wish. You can also run multiple separate Reticulum
|
||||
instances on the same physical system, in complete isolation from each other,
|
||||
or connected together.
|
||||
|
||||
In most cases, a single physical system will only need to run one Reticulum
|
||||
instance. This can either be launched at boot, as a system service, or simply
|
||||
be brought up when a program needs it. In either case, any number of programs
|
||||
running on the same system will automatically share the same Reticulum instance,
|
||||
if the configuration allows for it, which it does by default.
|
||||
|
||||
The entire configuration of Reticulum is found in the ``~/.reticulum/config``
|
||||
file. When Reticulum is first started on a new system, a basic, functional
|
||||
configuration file is created. The default configuration looks like this:
|
||||
|
||||
.. code::
|
||||
|
||||
# This is the default Reticulum config file.
|
||||
# You should probably edit it to include any additional,
|
||||
# interfaces and settings you might need.
|
||||
|
||||
# Only the most basic options are included in this default
|
||||
# configuration. To see a more verbose, and much longer,
|
||||
# configuration example, you can run the command:
|
||||
# rnsd --exampleconfig
|
||||
|
||||
|
||||
[reticulum]
|
||||
|
||||
# If you enable Transport, your system will route traffic
|
||||
# for other peers, pass announces and serve path requests.
|
||||
# This should only be done for systems that are suited to
|
||||
# act as transport nodes, ie. if they are stationary and
|
||||
# always-on. This directive is optional and can be removed
|
||||
# for brevity.
|
||||
|
||||
enable_transport = False
|
||||
|
||||
|
||||
# By default, the first program to launch the Reticulum
|
||||
# Network Stack will create a shared instance, that other
|
||||
# programs can communicate with. Only the shared instance
|
||||
# opens all the configured interfaces directly, and other
|
||||
# local programs communicate with the shared instance over
|
||||
# a local socket. This is completely transparent to the
|
||||
# user, and should generally be turned on. This directive
|
||||
# is optional and can be removed for brevity.
|
||||
|
||||
share_instance = Yes
|
||||
|
||||
|
||||
# If you want to run multiple *different* shared instances
|
||||
# on the same system, you will need to specify different
|
||||
# shared instance ports for each. The defaults are given
|
||||
# below, and again, these options can be left out if you
|
||||
# don't need them.
|
||||
|
||||
shared_instance_port = 37428
|
||||
instance_control_port = 37429
|
||||
|
||||
|
||||
# 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
|
||||
# an optional directive, and can be left out for brevity.
|
||||
# This behaviour is disabled by default.
|
||||
|
||||
panic_on_interface_error = No
|
||||
|
||||
|
||||
[logging]
|
||||
# Valid log levels are 0 through 7:
|
||||
# 0: Log only critical information
|
||||
# 1: Log errors and lower log levels
|
||||
# 2: Log warnings and lower log levels
|
||||
# 3: Log notices and lower log levels
|
||||
# 4: Log info and lower (this is the default)
|
||||
# 5: Verbose logging
|
||||
# 6: Debug logging
|
||||
# 7: Extreme logging
|
||||
|
||||
loglevel = 4
|
||||
|
||||
|
||||
# The interfaces section defines the physical and virtual
|
||||
# interfaces Reticulum will use to communicate on. This
|
||||
# section will contain examples for a variety of interface
|
||||
# types. You can modify these or use them as a basis for
|
||||
# your own config, or simply remove the unused ones.
|
||||
|
||||
[interfaces]
|
||||
|
||||
# This interface enables communication with other
|
||||
# link-local Reticulum nodes over UDP. It does not
|
||||
# need any functional IP infrastructure like routers
|
||||
# or DHCP servers, but will require that at least link-
|
||||
# local IPv6 is enabled in your operating system, which
|
||||
# should be enabled by default in almost any OS. See
|
||||
# the Reticulum Manual for more configuration options.
|
||||
|
||||
[[Default Interface]]
|
||||
type = AutoInterface
|
||||
interface_enabled = True
|
||||
|
||||
If Reticulum infrastructure already exists locally, you probably don't need to
|
||||
change anything, and you may already be connected to a wider network. If not,
|
||||
you will probably need to add relevant *interfaces* to the configuration, in
|
||||
order to communicate with other systems. It is a good idea to read the comments
|
||||
and explanations in the above default config. It will teach you the basic
|
||||
concepts you need to understand to configure your network. Once you have done that,
|
||||
take a look at the :ref:`Interfaces<interfaces-main>` chapter of this manual.
|
||||
|
||||
Included Utility Programs
|
||||
-------------------------
|
||||
|
||||
Reticulum includes a range of useful utilities, both for managing your Reticulum
|
||||
networks, and for carrying out common tasks over Reticulum networks, such as
|
||||
transferring files to remote systems, and executing commands and programs remotely.
|
||||
|
||||
If you often use Reticulum from several different programs, or simply want
|
||||
Reticulum to stay available all the time, for example if you are hosting
|
||||
a transport node, you might want to run Reticulum as a separate service that
|
||||
@@ -30,8 +150,8 @@ other programs, applications and services can utilise.
|
||||
The rnsd Utility
|
||||
================
|
||||
|
||||
To do so is very easy. Simply run the included ``rnsd`` command. When ``rnsd``
|
||||
is running, it will keep all configured interfaces open, handle transport if
|
||||
It is very easy to run Reticulum as a service. Simply run the included ``rnsd`` command.
|
||||
When ``rnsd`` is running, it will keep all configured interfaces open, handle transport if
|
||||
it is enabled, and allow any other programs to immediately utilise the
|
||||
Reticulum network it is configured for.
|
||||
|
||||
@@ -135,21 +255,22 @@ destinations on the Reticulum network.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-d] [-w seconds] [-v]
|
||||
[destination]
|
||||
|
||||
usage: rnpath [-h] [--config CONFIG] [--version] [-t] [-r] [-d] [-D] [-w seconds] [-v] [destination]
|
||||
|
||||
Reticulum Path Discovery Utility
|
||||
|
||||
|
||||
positional arguments:
|
||||
destination hexadecimal hash of the destination
|
||||
|
||||
destination hexadecimal hash of the destination
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-d, --drop remove the path to a destination
|
||||
-w seconds timeout before giving up
|
||||
-h, --help show this help message and exit
|
||||
--config CONFIG path to alternative Reticulum config directory
|
||||
--version show program's version number and exit
|
||||
-t, --table show all known paths
|
||||
-r, --rates show announce rate info
|
||||
-d, --drop remove the path to a destination
|
||||
-D, --drop-announces drop all queued announces
|
||||
-w seconds timeout before giving up
|
||||
-v, --verbose
|
||||
|
||||
|
||||
@@ -164,7 +285,7 @@ destinations will not have this option enabled, and will not be probable.
|
||||
.. code:: text
|
||||
|
||||
# Run rnprobe
|
||||
python3 -m RNS.Utilities.rnprobe example_utilities.echo.request 9382f334de63217a4278
|
||||
rnprobe example_utilities.echo.request 9382f334de63217a4278
|
||||
|
||||
# Example output
|
||||
Sent 16 byte probe to <9382f334de63217a4278>
|
||||
@@ -173,7 +294,7 @@ destinations will not have this option enabled, and will not be probable.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnprobe.py [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
||||
usage: rnprobe [-h] [--config CONFIG] [--version] [-v] [full_name] [destination_hash]
|
||||
|
||||
Reticulum Probe Utility
|
||||
|
||||
@@ -188,6 +309,109 @@ destinations will not have this option enabled, and will not be probable.
|
||||
-v, --verbose
|
||||
|
||||
|
||||
The rncp Utility
|
||||
================
|
||||
|
||||
The ``rncp`` utility is a simple file transfer tool. Using it, you can transfer
|
||||
files through Reticulum.
|
||||
|
||||
.. code:: text
|
||||
|
||||
# Run rncp on the receiving system, specifying which identities
|
||||
# are allowed to send files
|
||||
rncp --receive -a 940ea3f9e1037d38758f -a e28d5aee4317c24a9041
|
||||
|
||||
# From another system, copy a file to the receiving system
|
||||
rncp ~/path/to/file.tgz 256320d405d6d525d1e9
|
||||
|
||||
You can specify as many allowed senders as needed, or complete disable authentication.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rncp [-h] [--config path] [-v] [-q] [-p] [-r] [-b] [-a allowed_hash] [-n] [-w seconds] [--version] [file] [destination]
|
||||
|
||||
Reticulum File Transfer Utility
|
||||
|
||||
positional arguments:
|
||||
file file to be transferred
|
||||
destination hexadecimal hash of the receiver
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config path path to alternative Reticulum config directory
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
-r, --receive wait for incoming files
|
||||
-b, --no-announce don't announce at program start
|
||||
-a allowed_hash accept from this identity
|
||||
-n, --no-auth accept files from anyone
|
||||
-w seconds sender timeout before giving up
|
||||
--version show program's version number and exit
|
||||
-v, --verbose
|
||||
|
||||
|
||||
The rnx Utility
|
||||
================
|
||||
|
||||
The ``rnx`` utility is a basic remote command execution program. It allows you to
|
||||
execute commands on remote systems over Reticulum, and to view returned command
|
||||
output.
|
||||
|
||||
.. code:: text
|
||||
|
||||
# Run rnx on the listening system, specifying which identities
|
||||
# are allowed to execute commands
|
||||
rncp --listen -a 8111c4ff2968ab0c1286 -a 590256654482b4ba4038
|
||||
|
||||
# From another system, run a command
|
||||
rnx ad9a4c9da60089d41c29 "cat /proc/cpuinfo"
|
||||
|
||||
# Or enter the interactive mode pseudo-shell
|
||||
rnx ad9a4c9da60089d41c29 -x
|
||||
|
||||
# The default identity file is stored in
|
||||
# ~/.reticulum/identities/rnx, but you can use
|
||||
# another one, which will be created if it does
|
||||
# not already exist
|
||||
rnx ad9a4c9da60089d41c29 -i /path/to/identity
|
||||
|
||||
You can specify as many allowed senders as needed, or complete disable authentication.
|
||||
|
||||
.. code:: text
|
||||
|
||||
usage: rnx [-h] [--config path] [-v] [-q] [-p] [-l] [-i identity] [-x] [-b] [-a allowed_hash] [-n] [-N] [-d] [-m] [-w seconds] [-W seconds] [--stdin STDIN] [--stdout STDOUT] [--stderr STDERR] [--version]
|
||||
[destination] [command]
|
||||
|
||||
Reticulum Remote Execution Utility
|
||||
|
||||
positional arguments:
|
||||
destination hexadecimal hash of the listener
|
||||
command command to be execute
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--config path path to alternative Reticulum config directory
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
-l, --listen listen for incoming commands
|
||||
-i identity path to identity to use
|
||||
-x, --interactive enter interactive mode
|
||||
-b, --no-announce don't announce at program start
|
||||
-a allowed_hash accept from this identity
|
||||
-n, --noauth accept files from anyone
|
||||
-N, --noid don't identify to listener
|
||||
-d, --detailed show detailed result output
|
||||
-m mirror exit code of remote command
|
||||
-w seconds connect and request timeout before giving up
|
||||
-W seconds max result download time
|
||||
--stdin STDIN pass input to stdin
|
||||
--stdout STDOUT max size in bytes of returned stdout
|
||||
--stderr STDERR max size in bytes of returned stderr
|
||||
--version show program's version number and exit
|
||||
|
||||
|
||||
Improving System Configuration
|
||||
------------------------------
|
||||
|
||||
|
||||
@@ -122,6 +122,14 @@ Reticulum implements a range of generalised interface types that covers the comm
|
||||
|
||||
* UDP over IP networks
|
||||
|
||||
* Anything you can connect via stdio
|
||||
|
||||
* Reticulum can use external programs and pipes as interfaces
|
||||
|
||||
* This can be used to easily hack in virtual interfaces
|
||||
|
||||
* Or to quickly create interfaces with custom hardware
|
||||
|
||||
For a full list and more details, see the :ref:`Supported Interfaces<interfaces-main>` chapter.
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ setuptools.setup(
|
||||
'rnstatus=RNS.Utilities.rnstatus:main',
|
||||
'rnprobe=RNS.Utilities.rnprobe:main',
|
||||
'rnpath=RNS.Utilities.rnpath:main',
|
||||
|
||||
'rncp=RNS.Utilities.rncp:main',
|
||||
'rnx=RNS.Utilities.rnx:main',
|
||||
]
|
||||
},
|
||||
install_requires=['cryptography>=3.4.7', 'pyserial>=3.5', 'netifaces'],
|
||||
|
||||
Reference in New Issue
Block a user