Files
luci-app-zt-gateway/.omp/plans/allow-reactivate-current-gateway.md
Malar Invention 1e4a46c4bf setup wizard: fix routing, DHCP, WiFi AP, firewall, and deploy task
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
2026-07-13 11:11:19 +05:30

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:

  1. Backend (zt-gateway.uc line 184-187): switch method short-circuits with "Already on X" when UCI active_gateway matches the requested region — never runs the script
  2. UI (overview.js line 81): radio button for the active gateway is disabled, 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 replace is a no-op when the route already matches, conntrack flush is harmless, persist_all writes 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_ping fails 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

  1. Deploy to device: mise run deploy:install
  2. Open UI → amsterdam shows "active" → radio is now enabled
  3. Select amsterdam → click "Switch to selected" → should succeed and set up routing
  4. Verify routing: ip route show table 100 should show default via 10.11.12.3 dev ztk4jpk77j
  5. Verify NAT: iptables -t mangle -L -n should show WIBLAN mangle rules (for graceful mode)
  6. Test from LAN client: ping -I br-lan 10.11.12.3 should now work