The project's README from github.com/smthdagg/wificalling-location-gateway. As one markdown file: README.md.
An all-in-one Wi‑Fi Calling + Apple WLOC gateway for OpenWrt / ImmortalWrt
A standalone Rust service handles exit geolocation, WLOC response rewriting, certificate lifecycle, precise traffic isolation, and LuCI management — all integrated into a single installable package.
English Guide · 中文完整教程 · Security Policy · Development & Test Plan
[!IMPORTANT] This project is intended for authorized devices, networks, and test environments only. It does not prove that your carrier has enabled Wi‑Fi Calling, and it is not a substitute for real call verification; WLOC target locations must never be treated as emergency-call location. Follow local law, carrier terms, and Apple device-management requirements.

Wi‑Fi Calling Location Gateway brings two previously separate flows together on one router:
The core boundary of the project is "independent, precise, and revertible": WLOC uses its own process, UCI config, Unix socket, nftables table, and logs. It never takes over the Gateway's nftables table and never intercepts UDP 500/4500. When the protocol is unknown, Geo data is invalid, or the service is unhealthy, no default fake coordinates are produced.
.mobileconfig install entry and fingerprint verification.[Interface]/[Peer] config import, real-handshake health checks, and
WLOC follow-device exit probing through sing-box endpoints.sing-box.json and loaded into memory - unreferenced WireGuard
tunnels and protocol stacks stay off, so memory scales with the nodes
actually in use rather than the total configured (measured on AX6S:
sing-box RSS ~19-23 MB → ~15 MB).%%{init: {"flowchart": {"toolbar": false}}}%%
flowchart TD
I["Authorized test iPhone"] -->|"Wi‑Fi Calling · UDP 500/4500"| G["Wi‑Fi Calling Gateway"]
G --> N["Bound sing-box node"]
I -->|"Apple WLOC · TCP 443"| D["Precise DNS / nftables isolation"]
D --> R["wloc-service · Rust"]
R -->|"TLS + HTTP/2, strict upstream validation"| A["Apple WLOC"]
R --> P["Exit probe"]
P --> N
P --> X["Geo resolution & cache"]
X --> R
L["LuCI / rpcd"] -->|"root-only UDS API"| R
A location update roughly goes through these steps:
More detail: WLOC Service API, Threat model, and fail-open constraints.
| Layer | Implementation | Key constraints |
|---|---|---|
| Service runtime | Rust 2021, Tokio, static musl ELF | Rust 1.90; release LTO, opt-level=z, panic abort |
| TLS / HTTP | rustls, ring, tokio-rustls, h2 | TLS 1.2/1.3, ALPN h2, strict upstream cert & hostname validation |
| WLOC protocol | standalone clean-room protocol model with bounded parsing | Unknown, malformed, or oversized content is never guessed or partially rewritten |
| Control plane | wloc.service/v1, 4-byte BE framing, JSON, Unix socket |
16 KiB max frame, 2s total timeout, socket 0600, no TCP management port |
| Exit & location | sing-box exit probe, Geo primary/fallback + cache, manual coordinates | Invalid or stale data never falls back to default coordinates |
| OpenWrt integration | procd, UCI, rpcd, dnsmasq, firewall4/nftables | WLOC keeps its own table; never touches the Gateway table or UDP 500/4500 |
| Admin UI | LuCI JavaScript | Auto/manual switch, certificate, status, and log; sensitive fields sanitized |
| Build & release | OpenWrt SDK / Docker images pinned by digest | locked/offline compile, SHA-256, architecture tag cannot masquerade as all |
"Installable" is not the same as "verified with a real iPhone / Wi‑Fi Calling". The table separates the evidence levels:
| Platform | Arch | Package manager | Current evidence | Status |
|---|---|---|---|---|
| Redmi AX6S · ImmortalWrt 24.10.6 | MediaTek MT7622 / AArch64 | opkg | r13 service installed; one WIFICalling sing-box remained running, WLOC was intercepting, crash-recovery re-interception was observed, and post-test available memory remained above 32 MiB |
Docker + router + iPhone WLOC passed |
| OpenWrt 24.10.8 | x86_64 | opkg / IPK | Docker boot of init/ubus, integrated package install, service start, socket and v1 status checks | Install matrix passed |
| iStoreOS 24.10.5 | x86_64 | opkg / IPK | Same as above | Install matrix passed |
| OpenWrt 25.12.3 | x86_64 | apk / APK v3 | Same, using native APK v3, not a renamed IPK | Install matrix passed |
| Other OpenWrt / ImmortalWrt versions or CPUs | — | — | No device/SDK evidence yet | Not verified |
The runtime packages contain architecture-specific ELF files and must match the router CPU architecture. x86_64 packages are built with pinned SDKs; AX6S uses a separate AArch64 cortex-a53 toolchain. The formal Docker matrix installs both runtime variants for all three targets (six assets, eight runtime cases). Docker verifies install and boot; the AX6S row adds real router and iPhone WLOC evidence.
sing-box; Lite bundles its own runtime.Release v1.3.0-r13 provides two installation variants for each of three targets. Both belong to this project and contain the same WCG, WLOC, control tools, LuCI and saved UCI schema:
wificalling-location-gateway_1.3.0-r13_aarch64_cortex-a53.ipk, wificalling-location-gateway_1.3.0-r13_x86_64.ipk, wificalling-location-gateway-1.3.0-r13.apkwificalling-location-gateway-lite_1.3.0-r13_aarch64_cortex-a53.ipk, wificalling-location-gateway-lite_1.3.0-r13_x86_64.ipk, wificalling-location-gateway-lite-1.3.0-r13.apkprobe_interval is clamped to the validator window.Choose Standard when the firmware already supplies a suitable /usr/bin/sing-box. Choose Lite for constrained gateways such as AX6S: it stores a hash-pinned compressed sing-box in flash, transparently expands one shared copy into /tmp, and keeps only the single-worker/moderate-GC runtime profile without an artificial heap ceiling. Standard and Lite conflict intentionally and must not be installed together. Both preserve /etc/config/wificalling-gateway and /etc/config/wloc-service.
The variant suffix changes runtime ownership only; it does not create a separate product or restore split component packages.
Two ways to install:
Method A — package feed (recommended): add the signed feed and opkg install directly:
# Import the feed signing key (one-time)
wget -O /etc/opkg/keys/f7050198aa77cf15 \
https://raw.githubusercontent.com/smthdagg/Smthdagg-Repo-feeds/main/wloc.pub
# Add the feed and install
echo "src/gz wloc https://smthdagg.github.io/Smthdagg-Repo-feeds/wificalling-location-gateway" \
>> /etc/opkg/customfeeds.conf
opkg update && opkg install wificalling-location-gateway
Method B — manual download: grab the matching file from
Releases
and verify it against SHA256SUMS from the same release directory first.
Full instructions for both methods live in the
feed repository
(including the manual .apk install commands for OpenWrt 25.x).
opkg install /tmp/wificalling-location-gateway-lite_1.3.0-r13_aarch64_cortex-a53.ipk
Back up both UCI files first. On storage-constrained AX6S units, stop the services and remove the old integrated and sing-box packages before installing Lite; do not delete the saved UCI files. The r13 Lite package replaces the separate sing-box package and owns its transparent wrapper. /tmp is RAM: prefer the signed-feed upgrade (opkg update && opkg upgrade wificalling-location-gateway-lite), and when installing a local IPK, delete it right after installation (opkg install /tmp/x.ipk && rm -f /tmp/x.ipk) — leftover files in /tmp can push available memory below the cold-start memory gate.
opkg install /tmp/wificalling-location-gateway_1.3.0-r13_x86_64.ipk
# Or use the corresponding Lite asset when a bundled, bounded runtime is preferred.
apk add --allow-untrusted /tmp/wificalling-location-gateway-1.3.0-r13.apk
--allow-untrusted applies only to locally built packages that are not yet signed in a repository. Formal releases use repository signing; never rename an IPK into an APK.
test -S /var/run/wloc-service/control.sock
/usr/sbin/wloc-ctl status
/etc/init.d/wificalling-gateway status
logread -e wloc-service
The status response must contain "api_version":"wloc.service/v1". If the LuCI menu did not refresh, clear the browser cache and log back in instead of reinstalling packages for other architectures.
Configure in this order to avoid mixing network and location problems:
ASSURED in Wi‑Fi Calling Monitor & Log; always confirm with a real call in/out.wloc-service root CA under Settings → General → About → Certificate Trust Settings, and verify the fingerprint.Step-by-step guides:
./scripts/ci/verify.sh
This entry runs formatting, Clippy, unit/integration tests, Rust line coverage (minimum 80%), dependency audit, license policy, secret scan, release size, and repository contract checks. The formal 1.0 baseline is 69 Python tests passing, Rust line coverage ≥ 80%, and a release verification binary of about 0.97 MB.
OPENWRT_BIN_NAME=wloc-service \
OPENWRT_CROSS_CACHE_DIR=/tmp/wloc-rust-openwrt \
./scripts/ci/verify-rust-openwrt.sh
This pins the OpenWrt 24.10.8 mediatek/mt7622 toolchain, Rust version, and SHA-256, and verifies the AArch64 ELF, static linking, and size. See Rust OpenWrt cross-build notes.
./scripts/openwrt/build-x86_64-runtime.sh \
--out-dir "$PWD/dist/runtime/x86_64"
./scripts/openwrt/build-release-packages.sh \
--version 1.3.0 \
--release 9 \
--arch x86_64 \
--service-bin "$PWD/dist/runtime/x86_64/wloc-service" \
--ctl-bin "$PWD/dist/runtime/x86_64/wloc-ctl" \
--gateway-ipk /absolute/path/wificalling-location-gateway_1.3.0-r1_aarch64_cortex-a53.ipk \
--gateway-sha256 <verified-sha256> \
--out-dir "$PWD/dist/openwrt-release"
./scripts/openwrt/verify-docker-matrix.sh \
--dist-dir "$PWD/dist/wloc-openwrt-release-r13"
Builds use the official OpenWrt SDK pinned by digest; after dependency preparation, product compilation runs locked/offline with read-only sources in a network-disabled container. Full boundaries and results: OpenWrt packaging and Docker matrix.
A GitHub Linguist byte snapshot of the current main branch (2026-08-19). The router product runtime is mainly Rust, with JavaScript driving the LuCI admin UI and Shell handling OpenWrt lifecycle and network integration; Python is mostly used for reproducible builds, fixture governance, and CI.
pie showData
title GitHub Linguist language snapshot
"Rust · 46.36%" : 435146
"JavaScript · 26.18%" : 245706
"Shell · 17.28%" : 162237
"Python · 9.93%" : 93223
The numbers drift as main updates; whether LuCI JavaScript, docs, and generated/excluded files count depends on GitHub Linguist rules. Do not judge the project's primary language by helper-tool bytes alone.
src/ Rust service, protocol, TLS/H2, exit & Geo modules
openwrt/ procd/UCI, LuCI/rpcd, and OpenWrt package definitions
scripts/openwrt/ cross builds, dual-format packaging, Docker matrix
scripts/ci/ coverage, security, dependency, and repository gates
tests/ Rust, Python, JavaScript, and network-model tests
fixtures/ synthetic/sanitized fixture contracts and validators
docs/ API, security, deployment, testing, bilingual guides
.handoffs/ reproducible multi-agent handoff records