Reduce everything to macOS and Wireguard - #4
Merged
Merged
Conversation
### The deadlock chain
The helper runs on a **`new_current_thread`** Tokio runtime (`src/userspace_helper.rs:212`). Inside `wait_for_shutdown` it ticks once per second and, on macOS, runs a diagnostic probe **synchronously, inline, blocking the executor**:
- `src/userspace_helper.rs:392` — `next_diag_at = Instant::now()`
- `src/userspace_helper.rs:388` — `interval(1s)`; tokio's first `tick()` returns **immediately**
- `src/userspace_helper.rs:428-433` — first tick → `log_macos_dataplane_probe(...)` (a plain blocking call, not awaited)
- `src/userspace_helper.rs:451 → 505-508` — `read_wg_transfer_bytes` runs `wg show <iface> transfer` via `Command::output()` (no timeout), where `<iface>` = `control_interface_name` = `wgconf0`.
`wg show wgconf0 transfer` connects to `/var/run/wireguard/wgconf0.sock` — **gotatun's own in-process UAPI server**. Servicing that request requires gotatun's async `handle_api` task to run:
- gotatun's UAPI accept/IO runs on a dedicated `std::thread` (`uapi/mod.rs:113,203`), which calls **`send_sync` → `blocking_send` + `blocking_recv`** waiting for a response (`uapi/mod.rs:89-97`)
- the response is produced by `handle_api`, an **async task on the current-thread runtime** (`uapi/mod.rs:262`, `respond.send(...)`)
So:
```
runtime thread → blocked in Command::output() waiting for `wg` to exit
`wg` → blocked waiting for UAPI std-thread to write a reply
UAPI thread → blocking_recv, waiting for handle_api to answer
handle_api → needs the runtime thread to poll it … which is blocked in `wg`
```
A circular wait. The runtime thread is wedged forever, which means:
1. The `sigterm.recv()` / `sigint.recv()` arms (`:408`, `:404`) are never polled → **SIGTERM is silently ignored**.
2. The `control_socket_path.exists()` check (`:413`) never runs → socket removal wouldn't help either.
3. The device packet tasks (`handle_incoming`/`handle_outgoing`) can't be polled → **the tunnel passes no traffic**, and the routes it installed blackhole the LAN.
At disconnect, the privileged side dutifully `SIGTERM`s pid 78780, waits exactly 8 s, the wedged helper never moves, and you get `gotatun helper 78780 did not exit after SIGTERM; cleanup is incomplete` — the same pid surviving across both disconnect attempts. Cleanup (`cleanup_network`) never runs, so the routes + DNS overrides persist → LAN hijack → reboot. Same end state as before, different proximate failure — exactly your "different, but same system-broken result."
Extend `--mtu` support to include the userspace backend for `wgconf`. Update the privileged helper to pass MTU overrides to `gotatun` and add input validation for the MTU value. Update documentation with usage details and macOS-specific troubleshooting for userspace data planes.
Implement a framed response protocol that allows the privileged service to capture log output during request processing and stream it to the CLI. Updates the `gotatun` helper to write logs to a file that the service tails, ensuring setup and teardown activity is reported back to the user in real-time.
* fix/andi/cleanup_issue_mtu: Enable --mtu support for userspace WireGuard backends
Add `is_live()` to `ConnectionState` to verify if a tunnel process or interface is still active. Automatically prune stale or corrupted state files during connection attempts to prevent the system from getting wedged in a "connected" state after a reboot.
# Conflicts: # src/userspace_helper.rs
Address review feedback on the reconcile tick: - Skip (re-)applying tunnel DNS to a service whose original we failed to capture this tick, so we never overwrite DNS we cannot later restore. - Keep a service's saved original when its restore fails, so teardown (or a later tick) can retry instead of stranding the user's DNS. - Re-snapshot the fingerprint after mutating system DNS so the stored value matches reality and the next tick doesn't see a spurious diff.
…ng liveness 1. **`set_nonblocking(false)` on accepted sockets** (`privileged/mod.rs:145`): BSD/macOS inherits `O_NONBLOCK` from the listener to accepted connections, causing all `write_all()` calls to EAGAIN. Fixed. 2. **`wgconf status` stale-state false positive** (`wgconf/handlers.rs:153`): `cmd_status()` was printing "Connected:" from the state file without checking `is_live()`. After a reboot, the state file still exists but the tunnel is gone — so the autoconnect script saw "Connected:" and exited without reconnecting. Fixed by adding `&& conn.is_live()` to the filter.
Liveness was probed by an unprivileged Path::exists() on /var/run/wireguard/<iface>.sock, but that dir is 0750 root:daemon — so the check always returned false in user context (EACCES → exists() == false). cmd_status reported "Not connected" on a live tunnel, direct_connection_active() kept clearing "stale" state, and the autoconnect agent reconnected every 60s. Helpers piled up on the shared wgconf0.sock path (a departing helper's cleanup deleted the new helper's socket → control_socket_removed self-kill), collapsing the tunnel. - Add privileged InterfaceActive request; root answers the socket probe. - Route userspace::is_interface_active through the privileged service. - run_gotatun_up tears down an existing live helper before spawning, so there is never more than one and the socket can't be clobbered.
like these: `2026-06-17T17:48:59Z DEBUG tunmux::userspace_helper: userspace_helper_command command=route -q -n delete -inet -net 55.56.57.0/24`
…ion on unix socket`
pansen
force-pushed
the
chore/andi/wg_solo
branch
from
June 21, 2026 15:13
94299c5 to
1ce3f48
Compare
Add an "Other tunnels" section listing foreign utun/VPN interfaces (flagging 100.64/10 CGNAT as a likely Tailscale tailnet), a "System resolver" line comparing the effective global resolver against the tunnel's, and a tailscaled socket-presence hint — userspace Tailscale has no utun of its own, so the interface scan alone would miss it. Surfaces an interfering peer or a stale/foreign resolver at a glance instead of needing manual ifconfig/scutil/netstat spelunking.
pansen
force-pushed
the
chore/andi/wg_solo
branch
2 times, most recently
from
July 5, 2026 13:51
e49e31f to
92ec6f3
Compare
pansen
force-pushed
the
chore/andi/wg_solo
branch
from
July 5, 2026 16:09
92ec6f3 to
eb1282b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.