Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions reticulum-sidecar/patches/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,29 @@ Listed in `scripts/lib/ratspeak-overlay-apply-list.sh` and `RATSPEAK_PATCH_ENTRI

When ratspeak/rsReticulum exposes equivalent live TX queue depth on interface stats, remove this patch and the apply step.

## rsReticulum-pathless-link-exclude-rf.patch

Python `Transport.py` only transmits pathless `Destination.LINK` packets on `destination.attached_interface`. rsReticulum was pathless-broadcasting Link hashes (Keepalive / Lrrtt / Resource\*) onto every OUT iface, including flow-controlled RNodes, which filled the host TX queue during PN Sync. Prefer `link_table` when present; otherwise broadcast only to non-RF sinks (RNode name or bitrate under 100 kbps).

| Field | Value |
| ----- | ----- |
| **Base commit** | floated `origin/main` (regenerate; record short SHA in PR) |
| **Upstream PR** | none yet (mesh-client-local) |

**Touches:** `crates/rns-transport/src/actor/mod.rs`, `crates/rns-transport/src/actor/outbound.rs`

### Apply locally

```bash
./scripts/apply-rsReticulum-pathless-link-exclude-rf.sh
```

Listed in `scripts/lib/ratspeak-overlay-apply-list.sh` and `RATSPEAK_PATCH_ENTRIES` in `scripts/update.sh`.

### Sunset

When ratspeak/rsReticulum matches Python pathless-LINK attached-only (or equivalent) on floated `origin/main`, remove this patch and the apply step.

## rsLXMF-propagation-client-abort-transfer.patch

Adds `PropagationClient::abort_transfer` so Cancel / mid-transfer abort leaves the client **Idle**. Without it, a cancelled Sync can leave `/get` stuck busy and the next Sync returns `PROPAGATION_RETRIEVE_BUSY` forever (or Auto falsely concludes there are no PNs).
Expand Down
108 changes: 108 additions & 0 deletions reticulum-sidecar/patches/rsReticulum-pathless-link-exclude-rf.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
From: mesh-client
Subject: Exclude RF sinks from pathless Link broadcasts

Python Transport.py only transmits pathless LINK on attached_interface.
rsReticulum was flooding Keepalive/Resource onto flow-controlled RNodes.

--- a/crates/rns-transport/src/actor/mod.rs
+++ b/crates/rns-transport/src/actor/mod.rs
@@ -38,7 +38,18 @@
// duplex route. They never appear on the wire or in the interface registry.
const LOCAL_LINK_INITIATOR_INTERFACE: InterfaceId = InterfaceId::MAX;
const LOCAL_LINK_RESPONDER_INTERFACE: InterfaceId = InterfaceId::MAX - 1;
+
+/// LoRa / KISS-class sinks must not receive pathless Link floods (no path for
+/// link hashes). Bitrate ceiling ~100 kbps; also match common RNode names.
+fn iface_is_pathless_link_rf_sink(entry: &InterfaceEntry) -> bool {
+ const RF_BITRATE_CEILING_BPS: u64 = 100_000;
+ if entry.name.to_ascii_lowercase().contains("rnode") {
+ return true;
+ }
+ entry.bitrate > 0 && entry.bitrate < RF_BITRATE_CEILING_BPS
+}

+
#[derive(Clone)]
struct LocalLinkRoute {
initiator_tx: mpsc::Sender<crate::link_messages::DestinationEvent>,
@@ -1526,6 +1537,18 @@
fields(raw_len = raw.len()),
)]
fn broadcast_on_interfaces(&mut self, raw: &[u8], except: Option<InterfaceId>) -> bool {
+ self.broadcast_on_interfaces_filtered(raw, except, false)
+ }
+
+ /// Pathless Link egress must not flood flow-controlled RF (RNode / KISS-class
+ /// bitrates). Unattached senders still reach TCP hubs; RF only via Attached /
+ /// link_table / a real path.
+ fn broadcast_on_interfaces_filtered(
+ &mut self,
+ raw: &[u8],
+ except: Option<InterfaceId>,
+ exclude_rf_sinks: bool,
+ ) -> bool {
// Collect ids first so the borrow on self.interfaces ends before
// send_to_interface (which may mutate it via auto-deregister) runs.
let ids: Vec<InterfaceId> = self
@@ -1534,6 +1557,8 @@
.filter_map(|(&id, entry)| {
if except == Some(id) || !entry.direction.outbound {
None
+ } else if exclude_rf_sinks && iface_is_pathless_link_rf_sink(entry) {
+ None
} else {
Some(id)
}
--- a/crates/rns-transport/src/actor/outbound.rs
+++ b/crates/rns-transport/src/actor/outbound.rs
@@ -184,6 +184,50 @@
}
sent
} else {
+ // No path_table entry. Python Transport.py pathless LINK only
+ // transmits on destination.attached_interface — never floods
+ // every OUT iface. rsReticulum previously broadcast Link hashes
+ // (Keepalive / Lrrtt / Resource*) onto flow-controlled RNodes and
+ // filled the host TX queue. Prefer link_table; otherwise broadcast
+ // only to non-RF ifaces so TCP hubs still see unattached senders.
+ if parsed.flags.destination_type == rns_wire::flags::DestinationType::Link {
+ if let Some(target_interface) = self
+ .link_table
+ .get(&request.destination_hash)
+ .map(|e| e.interface_id)
+ {
+ let iface_name = self
+ .interfaces
+ .get(&target_interface)
+ .map(|e| e.name.as_str())
+ .unwrap_or("unknown");
+ tracing::info!(
+ dest = %hex::encode(request.destination_hash),
+ interface_id = target_interface,
+ interface_name = %iface_name,
+ "outbound: pathless Link routed via link_table"
+ );
+ return self.send_to_interface(target_interface, &request.raw);
+ }
+
+ let iface_names: Vec<&str> = self
+ .interfaces
+ .values()
+ .filter(|e| e.direction.outbound && !super::iface_is_pathless_link_rf_sink(e))
+ .map(|e| e.name.as_str())
+ .collect();
+ tracing::info!(
+ dest = %hex::encode(request.destination_hash),
+ broadcast_to = ?iface_names,
+ "outbound: pathless Link — excluding RF sinks (Python attached-only parity)"
+ );
+ return self.broadcast_on_interfaces_filtered(
+ &request.raw,
+ None,
+ /* exclude_rf_sinks */ true,
+ );
+ }
+
let iface_names: Vec<&str> = self
.interfaces
.values()
33 changes: 33 additions & 0 deletions scripts/apply-rsReticulum-pathless-link-exclude-rf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Apply mesh-client rsReticulum pathless-Link RF exclusion overlay.
# Pathless LINK hashes must not flood flow-controlled RNodes (Python attached-only).
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# shellcheck source=lib/apply-ratspeak-overlay.sh
source "${SCRIPT_DIR}/lib/apply-ratspeak-overlay.sh"
PATCH_FILE="${REPO_ROOT}/reticulum-sidecar/patches/rsReticulum-pathless-link-exclude-rf.patch"
RNS_DIR="${RS_RETICULUM_DIR:-${REPO_ROOT}/.rsstack/rsReticulum}"
MOD_RS="${RNS_DIR}/crates/rns-transport/src/actor/mod.rs"

if ! git -C "${RNS_DIR}" rev-parse --is-inside-work-tree > /dev/null 2>&1; then
echo "error: rsReticulum not found at ${RNS_DIR}" >&2
echo "Clone: git clone https://github.com/ratspeak/rsReticulum.git ${RNS_DIR}" >&2
exit 1
fi

if [[ ! -f "${PATCH_FILE}" ]]; then
echo "error: patch not found at ${PATCH_FILE}" >&2
exit 1
fi

if [[ -f "${MOD_RS}" ]] && grep -q 'iface_is_pathless_link_rf_sink' "${MOD_RS}"; then
echo "pathless-link-exclude-rf overlay already applied on rsReticulum @ $(git -C "${RNS_DIR}" rev-parse --short HEAD)"
exit 0
fi

if apply_ratspeak_overlay_or_die "${RNS_DIR}" "${PATCH_FILE}" "pathless-link-exclude-rf"; then
exit 0
fi
exit 1
1 change: 1 addition & 0 deletions scripts/lib/ratspeak-overlay-apply-list.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ RS_RETICULUM_APPLY_SCRIPTS=(
apply-rsReticulum-path-medium-slots.sh
apply-rsReticulum-inbound-raw-saturation-log.sh
apply-rsReticulum-interface-tx-queue-stats.sh
apply-rsReticulum-pathless-link-exclude-rf.sh
)

RS_LXMF_APPLY_SCRIPTS=(
Expand Down
1 change: 1 addition & 0 deletions scripts/update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ check_ratspeak_patches() {
'rsReticulum-discovery-announce-egress.patch|ratspeak/rsReticulum|19|rsReticulum discovery announce egress|https://github.com/ratspeak/rsReticulum/pull/19'
'rsReticulum-inbound-raw-saturation-log.patch|ratspeak/rsReticulum||rsReticulum inbound-raw saturation log|'
'rsReticulum-interface-tx-queue-stats.patch|ratspeak/rsReticulum||rsReticulum interface TX queue stats|'
'rsReticulum-pathless-link-exclude-rf.patch|ratspeak/rsReticulum||rsReticulum pathless Link exclude RF sinks|'
'rsLXMF-propagation-sync-peering.patch|ratspeak/rsLXMF|4|rsLXMF propagation sync peering|https://github.com/ratspeak/rsLXMF/pull/4'
'rsLXMF-propagation-node-policy-setters.patch|ratspeak/rsLXMF|6|rsLXMF PropagationNode policy setters|https://github.com/ratspeak/rsLXMF/pull/6'
'rsLXMF-propagation-node-deferred-messagestore-load.patch|ratspeak/rsLXMF||rsLXMF PropagationNode deferred messagestore load|'
Expand Down