Skip to content

luci-app-wificalling-gateway: add package - #694

Closed
smthdagg wants to merge 8 commits into
immortalwrt:masterfrom
smthdagg:luci-app-wificalling-gateway
Closed

luci-app-wificalling-gateway: add package#694
smthdagg wants to merge 8 commits into
immortalwrt:masterfrom
smthdagg:luci-app-wificalling-gateway

Conversation

@smthdagg

@smthdagg smthdagg commented Aug 8, 2026

Copy link
Copy Markdown

Description

LuCI application for per-device Wi-Fi Calling gateways: transparently routes selected LAN clients through a sing-box node (AnyTLS, Hysteria2, TUIC, VLESS Reality, VMess WebSocket, Trojan, WireGuard) with nftables TPROXY, observes ePDG/IPsec UDP 500/4500 evidence, and records handshake outcomes in an encrypted IMS activity log.

Key behaviors:

  • Device policies: one node per device, multiple fixed private IPv4 addresses per policy; independent routes through the node, follow_gateway is not intercepted.
  • Automatic DHCP static lease management: adding/removing a device policy auto-binds/cleans wfc_-prefixed dnsmasq host entries from the live lease table (tolerates iOS rotating private Wi-Fi MACs); the device policy table shows the live binding state.
  • WireGuard compatibility: endpoint form for sing-box ≥ 1.11, legacy outbound for 1.10.x (the legacy outbound was removed in 1.13.0); selected from the installed version at service start.
  • Share-link import: anytls://, hy2://, tuic://, vless://, vmess://, trojan://, wg:// parsed locally in the browser.
  • Node health: ICMP first, tcping fallback for TCP-based protocols when installed (no hard dependency).
  • Activity log: handshake success/failure and sustained encrypted communication only, per-device retention, can be disabled in Settings.
  • sing-box check before startup; mode 0600 for credential-bearing files; Simplified Chinese catalog shipped.

Exit-node note: TCP-based protocols (AnyTLS/VLESS/VMess/Trojan) are the reliable gateway exits; Hysteria2/TUIC (UDP/QUIC) "alive" only proves ICMP reachability.

How Has This Been Tested?

  • 48 local unit tests (compiler, monitor, firewall, dhcp-sync, import parser, packaging), sh -n / node --check / git diff --check clean.
  • sing-box check against real 1.10.0 / 1.11.7 / 1.12.0 / 1.13.18 binaries for both wireguard forms.
  • Verified end-to-end on a real ImmortalWrt 24.10.6 router: install, service, policy add/remove auto-binding, Save flow, node routing (egress IP matches node country).

Checklist

  • This PR targets the correct branch.
  • The commit messages follow the package naming convention.
  • No downstream raw embedded patch files modified or introduced.
  • Signed-off-by line present.

@smthdagg

smthdagg commented Aug 8, 2026

Copy link
Copy Markdown
Author

The Test Formalities failure is an upstream workflow issue, not a submission defect: formal.yml uses pull_request_target + actions/checkout@v5 without allow-unsafe-pr-checkout: true, so checkout refuses fork PR code before any commit check runs (Refusing to check out fork pull request code...). The same failure affects other fork PRs (e.g. #665). The commit itself follows the required conventions: subject luci-app-wificalling-gateway: add package and Signed-off-by: Smth Dagg <smthdagg@gmail.com>.

Per-device transparent Wi-Fi Calling gateway for OpenWrt / ImmortalWrt.

Forwards selected LAN clients through a selected sing-box node (AnyTLS,
Hysteria2, TUIC, VLESS Reality, VMess WebSocket) with one sing-box process
and nftables TPROXY, while other clients keep the normal gateway or
PassWall policy. Observes ePDG/IPsec UDP 500/4500 evidence and records
handshake success/failure and sustained encrypted communication in an
encrypted IMS activity log (toggleable from the Settings page).

Dependencies: luci-base, sing-box, firewall4, kmod-nft-tproxy,
kmod-nft-socket, ip-full, tcping.

Tested on ImmortalWrt 24.10.6 (Redmi AX6S, aarch64_cortex-a53); i18n path,
luci.mk LUCI_LC_ALIAS and dependencies are identical on ImmortalWrt 25.12.
Credential-bearing files are written with mode 0600; raw import links are
never logged.

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
@smthdagg
smthdagg force-pushed the luci-app-wificalling-gateway branch from 3fa84d1 to b3820ff Compare August 8, 2026 15:31
- Drop tcping dependency (not in feeds); make TCP probe optional.
- VLESS/VMess: emit TLS block for non-Reality TLS; fix alter_id JSON.
- Add security form field for VLESS; mask UUID in UI.
- Check firewall exit status; add device delimiter guard.
- firewall.sh: exit 0 on empty clients; remove unused monitor_interval.

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
- VLESS: auxiliary=$flow (not ${flow:-$alter_id}) so empty flow does
  not emit "flow":"0" which sing-box rejects.
- VMess: TLS gated on security field (f[16]=="tls") in addition to SNI;
  parseVmess reads raw.tls from the share link and sets security.
- firewall: on startup failure, call firewall.sh stop to clean up the
  TPROXY table before aborting (prevents blackhole with no listener).

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
nit fix: value('') rendered as a blank dropdown row; now value('', _('None')).

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
- security: depends on vless AND vmess (preserves imported tls across edits)
- tls(): omit server_name when empty (sing-box uses server address)
- delimiter guard: add $flow to checked fields

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
…start

Fix stale status page after device add/modify: clear both files at
startup so the status page shows only current configuration, not
leftover data from the previous run.

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
@smthdagg

Copy link
Copy Markdown
Author

Pushed the round-4 review fixes (03dc7ae): keep monitor.state as the per-device baseline (clear display state only), extend the delimiter guard to all | -delimited fields, fall back to the WS Host for TLS server_name when SNI is empty, reject Security=reality on VMess, and clear status.json in stop_service.

@smthdagg
smthdagg force-pushed the luci-app-wificalling-gateway branch from 03dc7ae to 1d2405b Compare August 10, 2026 07:03
- init.d: stop truncating monitor.state on start (it is the monitor's
  per-device baseline; clearing it fabricated handshake_success events
  on the first tick after every restart). Only status.json is cleared.
- init.d: extend the delimiter guard to every field interpolated into
  the | -delimited record (alpn, congestion, udp_mode, fingerprint,
  security, transport, path, pin_sha256).
- init.d: also clear status.json in stop_service for the stopped case.
- compiler.sh: when sni (f[7]) is empty, fall back to the WS Host
  (f[19]) as server_name for VLESS and VMess; the TLS decision itself
  still uses the original sni so plain ws stays cleartext.
- overview.js: reject Security = reality on VMess (no compiler arm).

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
@smthdagg
smthdagg force-pushed the luci-app-wificalling-gateway branch from 1d2405b to dbabc36 Compare August 10, 2026 08:21
- compiler: trojan outbound (password + TLS); wireguard endpoint form
  for sing-box >= 1.11 and legacy outbound for 1.10.x, selected from
  the installed sing-box version at service start (the legacy wireguard
  outbound was removed in 1.13.0)
- LuCI node form: wireguard private key / local address / reserved / MTU
- import: trojan:// and wg:// share links (Clash Meta / sing-box style)
- node-health: tcping fallback for trojan (TCP-based)
- i18n: new strings with Simplified Chinese translations

Signed-off-by: Smth Dagg <smthdagg@gmail.com>
@smthdagg
smthdagg force-pushed the luci-app-wificalling-gateway branch from 3227966 to 5ee9f14 Compare August 11, 2026 02:43
@smthdagg

Copy link
Copy Markdown
Author

Pushed 5ee9f146 with the same v1.6.0 update as the openwrt/luci PR (Trojan and WireGuard protocols, sing-box version-adaptive wireguard endpoint/outbound, trojan:// / wg:// import, i18n).

Note: the Test Formalities failure is still the upstream workflow issue (checked-out run log: actions/checkout@v5 refuses fork PR code in a pull_request_target workflow without allow-unsafe-pr-checkout: true); it affects every fork PR and is not a submission defect.

@1715173329

Copy link
Copy Markdown
Member

Closed in favor of upstream PR. The code will be merged back once upstream accepted it.

@1715173329 1715173329 closed this Aug 11, 2026
@smthdagg

Copy link
Copy Markdown
Author

Pushed DHCP lease auto-management (1.7.0):

  • dhcp-sync.sh: on service start, the plugin creates/updates wfc_-prefixed DHCP host bindings from the live lease table — a device leasing its policy IP gets that MAC pinned (this heals silently-broken hand-made bindings when iOS rotates its private Wi-Fi address, which left the policy rules matching a stale IP), and bindings whose policy was removed are dropped. Only plugin-created hosts are touched; user-managed hosts are left alone. dnsmasq restarts only when something changed.
  • LuCI: device policies gained a DHCP binding status column (Bound / MAC changed, rebind on reconnect / Not bound yet / Device offline / Following gateway), backed by read ACLs for the dhcp config and /tmp/dhcp.leases.
  • i18n updated; 45/45 local tests pass, sh -n / node --check / git diff --check clean.

@smthdagg

Copy link
Copy Markdown
Author

Same modal fix as the openwrt/luci PR: DHCP binding state now renders in the device policy edit modal (previously null); add/remove auto-binding verified end-to-end on the router.

@smthdagg

Copy link
Copy Markdown
Author

Same review-fix batch as the openwrt/luci PR: dhcp-host name sanitization (spaces/commas/quotes would make dnsmasq reject the host line), wireguard required/numeric validation with node-specific errors, wg:// base64 '+' restore, lease file read from UCI, local declarations, po re-sort. 48/48 tests pass.

@smthdagg

Copy link
Copy Markdown
Author

Same update as the openwrt/luci PR: sustained traffic events are now marked as likely_call (inferred, never decoded); docs explain DHCP static-binding rationale and the encrypted monitoring boundary. 49/49 tests pass.

@smthdagg

Copy link
Copy Markdown
Author

Hi @TianlingShen — this PR appears to have been closed without a comment. Could you let us know the reason (scope, review process, or something in the submission)? The package is actively maintained (v1.7.1): Trojan/WireGuard protocols, auto-managed DHCP static leases, and an encrypted-IMS activity log with call inference; all FormalityCheck jobs pass and the openwrt/luci counterpart PR (#8921) is open. Happy to restructure, squash, or re-submit as needed.

@smthdagg

Copy link
Copy Markdown
Author

Update: we've read the CONTRIBUTING guidance and understand now - immortalwrt/luci is a mirror of openwrt/luci, and normal luci applications should be submitted upstream at openwrt/luci (our PR #8921 is open there with CI green). We'll let that merge and ImmortalWrt will pick it up via the upstream sync. Thanks, and sorry for the noise on this repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants