[PR #2516] fix(windows): unblock WireGuard connect with large split-tunnel lists #2832

Open
opened 2026-10-14 21:05:35 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amnezia-client/pull/2516
Author: @kyoresuas
Created: 4/23/2026
Status: 🔄 Open

Base: dev ← Head: fix/windows-split-tunnel-stall


📝 Commits (2)

  • 0c44bab fix: unblock connect on Windows with large split-tunnel lists
  • 057e536 fix: prevent infinite connect on Windows reconnect with split tunnel

📊 Changes

7 files changed (+371 additions, -17 deletions)

View changed files

📝 client/daemon/daemon.cpp (+43 -14)
📝 client/daemon/daemon.h (+1 -0)
📝 client/daemon/wireguardutils.h (+20 -0)
📝 client/platforms/windows/daemon/windowsroutemonitor.cpp (+124 -3)
📝 client/platforms/windows/daemon/windowsroutemonitor.h (+7 -0)
📝 client/platforms/windows/daemon/wireguardutilswindows.cpp (+173 -0)
📝 client/platforms/windows/daemon/wireguardutilswindows.h (+3 -0)

📄 Description

Fixes #2248.

Currently, when connecting to a WireGuard server on Windows with a large split-tunnel list (e.g. the 11k-CIDR RU list from #2248), the client spends 5–20 minutes on "Connecting" before it finally reports "Connected". The same profile connects in 1–3 seconds on macOS, Android and iOS. Even after the first connect succeeds, reconnecting in the same session hangs on "Connecting" indefinitely.

This happens because Daemon::activate installs exclusion routes one at a time: every CreateIpForwardEntry2 call wakes wcmSvc (which router_win.cpp already works around for OpenVPN/Shadowsocks but not for WireGuard), and WindowsRouteMonitor::addExclusionRoute re-fetches the full routing table and re-runs updateCapturedRoutes for every prefix. On top of that, NotifyRouteChange2 fires a callback per row during interface bring-up; each callback queues a routeChanged slot that rescans everything, and Wintun's subnet-broadcast /32 entries (which QHostAddress::isBroadcast() does not recognize) keep failing with ERROR_OBJECT_ALREADY_EXISTS and get retried on every rescan - the daemon event loop fills up and Daemon::checkHandshake() never gets a slot, so the connected signal never reaches the GUI.

This PR adds batch entry points to WireguardUtils so WindowsRouteMonitor can install all exclusion prefixes against one routing-table snapshot per address family and suspend wcmSvc for the whole batch (matching router_win.cpp), debounces NotifyRouteChange2 callbacks through a 50 ms single-shot timer so bursts collapse into one rescan, and pins prefixes that return ERROR_OBJECT_ALREADY_EXISTS so they stop being retried. Other platforms fall back to the existing per-prefix path and are untouched.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/amnezia-vpn/amnezia-client/pull/2516 **Author:** [@kyoresuas](https://github.com/kyoresuas) **Created:** 4/23/2026 **Status:** 🔄 Open **Base:** `dev` ← **Head:** `fix/windows-split-tunnel-stall` --- ### 📝 Commits (2) - [`0c44bab`](https://github.com/amnezia-vpn/amnezia-client/commit/0c44babfa71a671ffddf987aa510739e9c3d44ab) fix: unblock connect on Windows with large split-tunnel lists - [`057e536`](https://github.com/amnezia-vpn/amnezia-client/commit/057e536cd4b6cfb5fe60490f35cc9656f2032f83) fix: prevent infinite connect on Windows reconnect with split tunnel ### 📊 Changes **7 files changed** (+371 additions, -17 deletions) <details> <summary>View changed files</summary> 📝 `client/daemon/daemon.cpp` (+43 -14) 📝 `client/daemon/daemon.h` (+1 -0) 📝 `client/daemon/wireguardutils.h` (+20 -0) 📝 `client/platforms/windows/daemon/windowsroutemonitor.cpp` (+124 -3) 📝 `client/platforms/windows/daemon/windowsroutemonitor.h` (+7 -0) 📝 `client/platforms/windows/daemon/wireguardutilswindows.cpp` (+173 -0) 📝 `client/platforms/windows/daemon/wireguardutilswindows.h` (+3 -0) </details> ### 📄 Description Fixes #2248. Currently, when connecting to a WireGuard server on Windows with a large split-tunnel list (e.g. the 11k-CIDR RU list from #2248), the client spends 5–20 minutes on "Connecting" before it finally reports "Connected". The same profile connects in 1–3 seconds on macOS, Android and iOS. Even after the first connect succeeds, reconnecting in the same session hangs on "Connecting" indefinitely. This happens because `Daemon::activate` installs exclusion routes one at a time: every `CreateIpForwardEntry2` call wakes `wcmSvc` (which `router_win.cpp` already works around for OpenVPN/Shadowsocks but not for WireGuard), and `WindowsRouteMonitor::addExclusionRoute` re-fetches the full routing table and re-runs `updateCapturedRoutes` for every prefix. On top of that, `NotifyRouteChange2` fires a callback per row during interface bring-up; each callback queues a `routeChanged` slot that rescans everything, and Wintun's subnet-broadcast `/32` entries (which `QHostAddress::isBroadcast()` does not recognize) keep failing with `ERROR_OBJECT_ALREADY_EXISTS` and get retried on every rescan - the daemon event loop fills up and `Daemon::checkHandshake()` never gets a slot, so the `connected` signal never reaches the GUI. This PR adds batch entry points to `WireguardUtils` so `WindowsRouteMonitor` can install all exclusion prefixes against one routing-table snapshot per address family and suspend `wcmSvc` for the whole batch (matching `router_win.cpp`), debounces `NotifyRouteChange2` callbacks through a 50 ms single-shot timer so bursts collapse into one rescan, and pins prefixes that return `ERROR_OBJECT_ALREADY_EXISTS` so they stop being retried. Other platforms fall back to the existing per-prefix path and are untouched. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#2832
No description provided.