Setup script fixes: - _cidr_to_mask: pad to 4 octets (/24 -> 255.255.255.0) - UCI quoting: remove embedded shell quotes from uci set calls - Bridge ports: auto-detect zt* interface instead of hardcoding ztabc0 - Bridge netmask: default to /23 (255.255.254.0) for ZT+WIBLAN - DHCP/WiFi AP: reference interface name (zt_wiblan) not device name (br_zt) - Firewall zone: add zt_wiblan to LAN zone for nftables fw4 - ZT IP persistence: ensure ZT-assigned IP stays on interface for ARP - Exit gateway routing: table 100/101 route via exit gateway, not self - New setup-wifi-ap subcommand for WIBLAN WiFi AP UBUS handler: - Add setup-wifi-ap to validation regex and error message Deploy task: - Auto-discover files from root/ and htdocs/ instead of hardcoded list - Clear LuCI cache before restarting services Documentation: - New docs/SETUP-GATEWAY.md with architecture, config, pitfalls, checklist - Updated docs/INSTALL.md with deploy task and setup wizard sections - Updated docs/PROGRESS.md with session log and learnings
2.8 KiB
2.8 KiB
Plan: Allow re-activating the current gateway from the UI
Problem
The UI shows "active" on a gateway based solely on the UCI active_gateway config value, but the switch script (zt-gateway-switch) may never have been run — meaning no routing tables, no masquerade, no actual traffic forwarding. The user sees "active" and assumes it works.
Two blocking issues prevent fixing this from the UI:
- Backend (
zt-gateway.ucline 184-187):switchmethod short-circuits with "Already on X" when UCIactive_gatewaymatches the requested region — never runs the script - UI (
overview.jsline 81): radio button for the active gateway isdisabled, so the user can't even select it to click "Switch to selected"
Fix
Two files, one change each.
1. Backend: root/usr/share/rpcd/ucode/zt-gateway.uc
Remove the "Already on X" early return (lines 184-187):
// DELETE these lines:
const current_region = read_active_region();
if (current_region === region && !drain_active()) {
return { success: true, message: `Already on ${region}.` };
}
Why this is safe:
do_force()in the switch script is idempotent:ip route replaceis a no-op when the route already matches, conntrack flush is harmless,persist_allwrites the same values- The backend still updates UCI after the script runs (line 198-199) — setting the same value is harmless
- If the gateway is unreachable,
preflight_pingfails with exit 2 and the backend returns the error — same as switching to any other unreachable gateway
2. UI: htdocs/luci-static/resources/view/zt-gateway/overview.js
Remove the disabled attribute from the active gateway's radio (line 81):
Change:
disabled: isActive || null
To:
// Remove this line entirely (or keep disabled only during an active drain)
Why this is safe:
- The user can now select the active gateway and click "Switch to selected"
- The backend runs the switch script which sets up routing
- If routing is already correct, the script is a harmless idempotent no-op
- The "Switch to selected" button text still makes sense — it re-applies the gateway config
Files to modify
| File | Change |
|---|---|
root/usr/share/rpcd/ucode/zt-gateway.uc |
Remove lines 184-187 (early return) |
htdocs/luci-static/resources/view/zt-gateway/overview.js |
Remove disabled on line 81 |
Verification
- Deploy to device:
mise run deploy:install - Open UI → amsterdam shows "active" → radio is now enabled
- Select amsterdam → click "Switch to selected" → should succeed and set up routing
- Verify routing:
ip route show table 100should showdefault via 10.11.12.3 dev ztk4jpk77j - Verify NAT:
iptables -t mangle -L -nshould show WIBLAN mangle rules (for graceful mode) - Test from LAN client:
ping -I br-lan 10.11.12.3should now work