OpenWrt x86_64 add-on project for marine-stable Wi-Fi / 5G / satellite multi-WAN scoring, controlled automatic network switching, user-selected local per-flow aggregation guidance, and a custom LuCI page.
This project does not modify mwan3 internals. It installs a separate LuCI application and backend service that read link status, probe quality, and display scores. Example network and mwan3 snippets are provided under examples/, but they are not installed by default.
The default profile is marine_stable: controlled automatic switching, conservative primary/backup behavior, long dwell/confirmation windows, rollback-friendly policy application, and aggregation only across WAN links selected by the user. Satellite is not selected for aggregation by default. A faster opt-in fast_marine example is provided for 4 Wi-Fi + 3 5G + 1 satellite deployments that need second-level emergency failover while keeping normal quality switches guarded.
Without a cloud aggregation server or a transport/session anchor, this project does not promise true single-session seamless bonding. Multiple users, connections, or app flows can be spread across healthy WAN links by mwan3; one existing TCP session is not guaranteed to survive an IP-changing WAN switch.
- Example OpenWrt
network,mwan3, andlinkscoreconfiguration underexamples/. linkscore: backend scoring service package with configurable aggregation selection and guarded mwan3 policy apply/rollback.luci-app-linkscore: LuCI control panel underServices -> Link Router.luci-theme-linkrouter: branded LuCI theme package.- Build helper scripts for x86_64 Image Builder workflows.
The first runnable milestone is intentionally conservative:
- Build or install LuCI,
mwan3, and the custom packages. - Display read-only link scores for configured WAN links such as
wan_wifi,wan_5g, andwan_sat. - Display whether the recommended mode is
switch,failover, or user-selectedaggregate. - Let users choose which WAN links may participate in aggregation.
- Apply recommended mwan3 policies through the guarded
linkscoreapply/rollback path.
See wifi_5g_satellite_switching_algorithm_plan.md for the full implementation plan.
Implemented in this repository:
- OpenWrt x86_64 add-on project scaffold.
- Example
network,mwan3, andlinkscoreconfigs. linkscorepackage with a shell/awk scoring backend and rpcd entrypoint.settingsandconfigurecommands for aggregation link selection, auto-switch enablement, and aggregation thresholds.healthcommand and rpcd method for daemon runtime visibility (running state, memory guard view, and last tick snapshot).eventscommand and rpcd method for event-log querying with level/limit filtering and per-level counters.- Optional MQTT telemetry publishing for remote fleet dashboards, including status, health, link score, and event summary payloads.
- MQTT publishing uses the lightweight
mosquitto_pubclient, supports short one-shot publishing or long-lived dashboard-visible publishing, optional per-device topic directories for multi-vessel fleets, optional payload mode (full/compact), sends payloads through stdin, and keeps MQTT passwords out of process arguments. - Dynamic configured WAN link discovery from
config linksections, including 8-WAN examples for 4 Wi-Fi, 3 5G, and 1 satellite link. fast_marineexample tuning for faster emergency failover without modifyingmwan3source code.tickauto-switch guard that waits for a stable pending action, respects confirmation and dwell timers, then uses the guarded apply path.- Emergency satellite failover path for cases where only the satellite WAN remains usable.
- Wi-Fi instability scoring for jitter tail, burst loss, SNR, RSSI, channel busy time, and retransmit rate.
- Guarded
applyandrollbackcommands that validate allowed policies, back up/etc/config/mwan3, update the default mwan3 rule, and reload mwan3. - Fixture-based behavior test for the scoring backend.
luci-app-linkscorestatus page.- OpenClash-style panel sections for overview, links, aggregation selection, auto-switch guard, and logs.
luci-theme-linkroutertheme package with placeholder Link Router logo assets.
tests/test-linkscore.sh
tests/test-rpcd-contract.sh
tests/test-package-manifest.sh
tests/test-install-safety.sh
tests/test-policy-apply.sh
tests/test-settings-configure.sh
tests/test-auto-switch-tick.sh
tests/test-emergency-switch.sh
tests/test-emergency-sat-failover.sh
tests/test-dynamic-8wan-fast-marine.sh
tests/test-jsonfilter-runtime.sh
tests/test-link-type-boundaries.sh
tests/test-luci-save-refresh.sh
tests/test-health-snapshot.sh
tests/test-events.sh
tests/test-mqtt-telemetry.sh
scripts/mqtt-soak-sim.sh
scripts/mqtt-recovery-stats.sh
find scripts package/linkrouter tests -type f -perm -111 -print0 | xargs -0 -I{} sh -n {}
find . -name '*.json' -not -path './.git/*' -print0 | xargs -0 jq empty
node --check package/linkrouter/luci-app-linkscore/htdocs/luci-static/resources/view/status/linkscore.js