[PR #2877] feat(windows): native ARM64 support and OpenVPN data channel offload #2999

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

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amnezia-client/pull/2877
Author: @yesihaveidea
Created: 7/25/2026
Status: 🔄 Open

Base: dev ← Head: feat/win-arm64-v5


📝 Commits (10+)

  • 358e472 fix(windows): tun cleanup, IPv6 routing result, tunnel logger and xray stop order
  • e6793e6 feat(build): Windows ARM64 (armv8) support in recipes and packaging
  • 11433c8 feat(ci): windows-11-arm bake and build jobs
  • 998bb5c feat(openvpn): ovpn-dco-win data channel offload with tap-windows6 fallback
  • 2a084b4 fix(build): vcvars discovery in build.bat and mc.exe resolution for openvpnserv
  • 1731579 fix(installer): recreate existing service on upgrade instead of failing
  • 1178887 chore(deps): bump openvpn 2.7.0 -> 2.7.5
  • 67a0bc3 fix(openvpn): install tap-windows6 on demand when openvpn needs it
  • 9dd21e4 chore(deps): bump tun2socks 2.6.0 -> 2.7.0
  • ccde4c1 fix(wireguard): preserve AmneziaWG obfuscation params across config model

📊 Changes

33 files changed (+920 additions, -56 deletions)

View changed files

📝 .github/workflows/deploy.yml (+128 -0)
📝 client/core/configurators/openVpnConfigurator.cpp (+5 -0)
📝 client/core/controllers/updateController.cpp (+4 -0)
📝 client/core/models/protocols/wireGuardProtocolConfig.cpp (+102 -2)
📝 client/core/models/protocols/wireGuardProtocolConfig.h (+29 -1)
📝 client/core/protocols/openVpnProtocol.cpp (+56 -13)
📝 client/core/protocols/openVpnProtocol.h (+11 -0)
📝 client/core/protocols/xrayProtocol.cpp (+9 -4)
📝 client/platforms/windows/daemon/windowstunnellogger.cpp (+6 -0)
📝 cmake/CPack.cmake (+25 -1)
📝 conanfile.py (+3 -2)
📝 deploy/build.bat (+10 -4)
📝 deploy/deploy_s3.sh (+2 -1)
📝 deploy/installer/qif/componentscript.js (+24 -8)
📝 ipc/ipc_interface.rep (+1 -0)
📝 ipc/ipcserver.cpp (+21 -0)
📝 ipc/ipcserver.h (+1 -0)
📝 recipes/amnezia-xray-bindings/conanfile.py (+36 -4)
📝 recipes/awg-windows/conanfile.py (+11 -1)
📝 recipes/go/conandata.yml (+4 -1)

...and 13 more files

📄 Description

Native Windows ARM64 (armv8) support for the Conan-based tree, plus the OpenVPN data-channel-offload driver that OpenVPN 2.7 needs on that platform.

Closes #2401, addresses #882 and the Windows part of #1429.

Built and runtime-tested on a Snapdragon X Elite laptop (Windows 11 26200): AmneziaWG, WireGuard, Xray and OpenVPN all connect; installers produced by both the IFW and the WiX generators install and upgrade cleanly.

Windows fixes that are not ARM64-specific

These stand on their own and affect x64 users too:

  • RouterWin::deleteTun() was never implemented, so Router::deleteTun() silently returned true on Windows. A tun2socks process that dies without a graceful stop leaves its wintun devnode behind, and the next Xray connection then hangs trying to reuse it. The new implementation removes the devnode by its exact adapter name, read from wintun's private DEVPKEY_Wintun_Name; matching on SPDRP_FRIENDLYNAME would not do, because wintun stores the tunnel type there and every wintun-based product shares it.
  • XrayProtocol::stop() deleted the TUN before killing tun2socks. Removing a device that is still held open only marks it pending-removal, which is exactly how the orphan above is created. The delete now happens after the process is reaped.
  • RouterWin::StartRoutingIpv6() dropped its QtConcurrent future without waitForFinished(), so it always reported failure and the IPv6 restore raced the rest of the shutdown.
  • WindowsTunnelLogger polled an unnamed file every 250 ms when the WireGuard ring-log path is absent, filling the service log with QFSFileEngine::open: No file name specified.
  • deploy/build.bat never called vcvarsall: the vswhere block reads %VS_INSTALLER_PATH% inside the same parenthesised block that sets it, so the variable expands empty. CI hides this because msvc-dev-cmd has already set the environment; local builds fail to link.
  • The openvpn recipe fails to configure when the Windows SDK bin directory is not on PATH: src/openvpnserv calls find_program(mc.exe) and aborts. The recipe now resolves the message compiler explicitly and passes it as MC_COMPILER.
  • The IFW installer aborted with exit code 1073 on upgrades — sc create returns ERROR_SERVICE_EXISTS when the service is still registered. It is now stopped and deleted first.
  • vc_redist was never shipped. componentscript.js has always tried to execute @TargetDir@\vc_redist.<arch>.exe, but nothing staged that file; the redistributable is now downloaded at configure time and installed, and the architecture is picked from the real CPU rather than a "64" substring test.

ARM64 support

  • recipes/go: Windows/armv8 toolchain entry, needed as soon as a native ARM64 machine builds anything Go-based.
  • recipes/win-split-tunnel: the four sha256 values were the x86_64 ones for both architectures. They are per-architecture now — Mullvad has published aarch64-pc-windows-msvc binaries since 2024, so split tunnelling works on ARM64 as well.
  • New recipes/llvm-mingw: mingw-builds and msys2 have no armv8 builds on conan-center, and CGO for GOARCH=arm64 needs an aarch64-w64-mingw32 compiler. awg-windows and amnezia-xray-bindings use it for armv8; the same package serves both a native ARM64 host and an x64 cross-build.
  • recipes/tun2socks and recipes/amnezia-xray-bindings build directly with go build on Windows/armv8 instead of going through the msys2 Makefile wrapper.
  • deploy/build.bat handles a native ARM64 host (PROCESSOR_ARCHITECTURE=ARM64) and passes the generator platform explicitly, so an x64 machine cross-building -arch arm64 no longer produces x64 binaries.
  • cmake/CPack.cmake derives the package suffix and CPACK_WIX_ARCHITECTURE from the target architecture; updateController and deploy_s3.sh follow suit so ARM64 installs do not self-update into the x64 installer.
  • .github/workflows/deploy.yml: bake and build jobs on windows-11-arm, which is generally available for public repositories since August 2025.

OpenVPN data channel offload

OpenVPN 2.7 removed wintun, and tap-windows6 is the only remaining alternative to ovpn-dco. The client had no way to provision either driver, so OpenVPN could not work on a fresh ARM64 install at all.

  • New recipes/ovpn-dco-win 2.8.3 ships the Microsoft-attestation-signed driver byte-for-byte from the official release (win10 and win11 flavours, amd64 and arm64). I verified the SHA256 of these files against the ones inside OpenVPN-2.7.5-I001-arm64.msi: they are identical, so this is the same pairing upstream ships. The driver is GPLv2, so redistribution is fine; nothing is rebuilt or re-signed.
  • The openvpn recipe also packages tapctl.exe. Without OpenVPN's own interactive service, adapter creation has to happen on our side.
  • New DcoController installs the driver with pnputil on first use, picks the win10/win11 flavour by OS build, and creates the AmneziaDCO adapter with tapctl. IpcServer::checkAndInstallDriver() tries DCO first and falls back to TapController.
  • When a config is not DCO-capable (compression, --fragment, a proxy, a non-AEAD data cipher, --dev tap), openvpn falls back to tap-windows6 on its own and dies with "There are no TAP-Windows or ovpn-dco adapters on this system". That message now triggers an on-demand install of the legacy driver and one retry.
  • ncp-disable is stripped from local configs: the option was removed in OpenVPN 2.6 and 2.7 rejects it outright, so any server configured with it currently produces a hard failure on Windows.

Dependency bumps

  • openvpn 2.7.0 → 2.7.5 — nine CVEs, and it is the userspace release that openvpn-build pairs with driver 2.8.3.
  • tun2socks 2.6.0 → 2.7.0.

Notes for reviewers

The commits are ordered so they can be reviewed — and if you prefer, cherry-picked — independently; only the ARM64 recipe work and the DCO work touch the same files.

Qt is unchanged at 6.10.1 (win64_msvc2022_arm64_cross_compiled plus the same qtremoteobjects qt5compat qtshadertools module set the x64 job uses).

awg-windows still has no arm64 target in its own build.cmd; the recipe drives the Go build directly, so nothing is blocked, but a one-line addition upstream would mirror what wireguard-windows already does.


Note on scope: this branch also carries one commit that is not ARM64-specific and is submitted independently as #2882 — WireGuardClientConfig::fromJson/toJson was not round-tripping the AmneziaWG obfuscation parameters, so a WireGuard profile that had obfuscation enabled silently degrades to plain WireGuard after an upgrade. It rides along here only so the ARM64 build is self-contained; please review and merge it in #2882 — the commit is identical, so it drops out of this range once #2882 lands on dev.


🔄 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/2877 **Author:** [@yesihaveidea](https://github.com/yesihaveidea) **Created:** 7/25/2026 **Status:** 🔄 Open **Base:** `dev` ← **Head:** `feat/win-arm64-v5` --- ### 📝 Commits (10+) - [`358e472`](https://github.com/amnezia-vpn/amnezia-client/commit/358e472f4765699d6c42fe00602faa82de707168) fix(windows): tun cleanup, IPv6 routing result, tunnel logger and xray stop order - [`e6793e6`](https://github.com/amnezia-vpn/amnezia-client/commit/e6793e68192568880be25e4422cb094b3ce02dec) feat(build): Windows ARM64 (armv8) support in recipes and packaging - [`11433c8`](https://github.com/amnezia-vpn/amnezia-client/commit/11433c8c7940eca26fe67d09cd75027836f05383) feat(ci): windows-11-arm bake and build jobs - [`998bb5c`](https://github.com/amnezia-vpn/amnezia-client/commit/998bb5c05823745f80fd5e8aa68b47b2725183aa) feat(openvpn): ovpn-dco-win data channel offload with tap-windows6 fallback - [`2a084b4`](https://github.com/amnezia-vpn/amnezia-client/commit/2a084b401fb5c9370204c050be200d6d26762c78) fix(build): vcvars discovery in build.bat and mc.exe resolution for openvpnserv - [`1731579`](https://github.com/amnezia-vpn/amnezia-client/commit/173157959f508b62a077389ea1084d7074e53876) fix(installer): recreate existing service on upgrade instead of failing - [`1178887`](https://github.com/amnezia-vpn/amnezia-client/commit/11788876504668eef217f7ac50204cec8f40755b) chore(deps): bump openvpn 2.7.0 -> 2.7.5 - [`67a0bc3`](https://github.com/amnezia-vpn/amnezia-client/commit/67a0bc30f22128a7b24532db806a41d9823c3fb3) fix(openvpn): install tap-windows6 on demand when openvpn needs it - [`9dd21e4`](https://github.com/amnezia-vpn/amnezia-client/commit/9dd21e46199e3b8a5378f79190674f35d5844c1c) chore(deps): bump tun2socks 2.6.0 -> 2.7.0 - [`ccde4c1`](https://github.com/amnezia-vpn/amnezia-client/commit/ccde4c10e4985e1c76dc23afefbdb80f70780bf8) fix(wireguard): preserve AmneziaWG obfuscation params across config model ### 📊 Changes **33 files changed** (+920 additions, -56 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/deploy.yml` (+128 -0) 📝 `client/core/configurators/openVpnConfigurator.cpp` (+5 -0) 📝 `client/core/controllers/updateController.cpp` (+4 -0) 📝 `client/core/models/protocols/wireGuardProtocolConfig.cpp` (+102 -2) 📝 `client/core/models/protocols/wireGuardProtocolConfig.h` (+29 -1) 📝 `client/core/protocols/openVpnProtocol.cpp` (+56 -13) 📝 `client/core/protocols/openVpnProtocol.h` (+11 -0) 📝 `client/core/protocols/xrayProtocol.cpp` (+9 -4) 📝 `client/platforms/windows/daemon/windowstunnellogger.cpp` (+6 -0) 📝 `cmake/CPack.cmake` (+25 -1) 📝 `conanfile.py` (+3 -2) 📝 `deploy/build.bat` (+10 -4) 📝 `deploy/deploy_s3.sh` (+2 -1) 📝 `deploy/installer/qif/componentscript.js` (+24 -8) 📝 `ipc/ipc_interface.rep` (+1 -0) 📝 `ipc/ipcserver.cpp` (+21 -0) 📝 `ipc/ipcserver.h` (+1 -0) 📝 `recipes/amnezia-xray-bindings/conanfile.py` (+36 -4) 📝 `recipes/awg-windows/conanfile.py` (+11 -1) 📝 `recipes/go/conandata.yml` (+4 -1) _...and 13 more files_ </details> ### 📄 Description Native Windows ARM64 (armv8) support for the Conan-based tree, plus the OpenVPN data-channel-offload driver that OpenVPN 2.7 needs on that platform. Closes #2401, addresses #882 and the Windows part of #1429. Built and runtime-tested on a Snapdragon X Elite laptop (Windows 11 26200): AmneziaWG, WireGuard, Xray and OpenVPN all connect; installers produced by both the IFW and the WiX generators install and upgrade cleanly. ## Windows fixes that are not ARM64-specific These stand on their own and affect x64 users too: * **`RouterWin::deleteTun()` was never implemented**, so `Router::deleteTun()` silently returned `true` on Windows. A tun2socks process that dies without a graceful stop leaves its wintun devnode behind, and the next Xray connection then hangs trying to reuse it. The new implementation removes the devnode by its exact adapter name, read from wintun's private `DEVPKEY_Wintun_Name`; matching on `SPDRP_FRIENDLYNAME` would not do, because wintun stores the *tunnel type* there and every wintun-based product shares it. * **`XrayProtocol::stop()` deleted the TUN before killing tun2socks.** Removing a device that is still held open only marks it pending-removal, which is exactly how the orphan above is created. The delete now happens after the process is reaped. * **`RouterWin::StartRoutingIpv6()` dropped its `QtConcurrent` future** without `waitForFinished()`, so it always reported failure and the IPv6 restore raced the rest of the shutdown. * **`WindowsTunnelLogger` polled an unnamed file every 250 ms** when the WireGuard ring-log path is absent, filling the service log with `QFSFileEngine::open: No file name specified`. * **`deploy/build.bat` never called `vcvarsall`**: the vswhere block reads `%VS_INSTALLER_PATH%` inside the same parenthesised block that sets it, so the variable expands empty. CI hides this because `msvc-dev-cmd` has already set the environment; local builds fail to link. * **The `openvpn` recipe fails to configure** when the Windows SDK bin directory is not on `PATH`: `src/openvpnserv` calls `find_program(mc.exe)` and aborts. The recipe now resolves the message compiler explicitly and passes it as `MC_COMPILER`. * **The IFW installer aborted with exit code 1073 on upgrades** — `sc create` returns `ERROR_SERVICE_EXISTS` when the service is still registered. It is now stopped and deleted first. * **`vc_redist` was never shipped.** `componentscript.js` has always tried to execute `@TargetDir@\vc_redist.<arch>.exe`, but nothing staged that file; the redistributable is now downloaded at configure time and installed, and the architecture is picked from the real CPU rather than a `"64"` substring test. ## ARM64 support * `recipes/go`: Windows/armv8 toolchain entry, needed as soon as a native ARM64 machine builds anything Go-based. * `recipes/win-split-tunnel`: the four sha256 values were the x86_64 ones for both architectures. They are per-architecture now — Mullvad has published `aarch64-pc-windows-msvc` binaries since 2024, so split tunnelling works on ARM64 as well. * **New `recipes/llvm-mingw`**: `mingw-builds` and `msys2` have no armv8 builds on conan-center, and CGO for `GOARCH=arm64` needs an `aarch64-w64-mingw32` compiler. `awg-windows` and `amnezia-xray-bindings` use it for armv8; the same package serves both a native ARM64 host and an x64 cross-build. * `recipes/tun2socks` and `recipes/amnezia-xray-bindings` build directly with `go build` on Windows/armv8 instead of going through the msys2 Makefile wrapper. * `deploy/build.bat` handles a native ARM64 host (`PROCESSOR_ARCHITECTURE=ARM64`) and passes the generator platform explicitly, so an x64 machine cross-building `-arch arm64` no longer produces x64 binaries. * `cmake/CPack.cmake` derives the package suffix and `CPACK_WIX_ARCHITECTURE` from the target architecture; `updateController` and `deploy_s3.sh` follow suit so ARM64 installs do not self-update into the x64 installer. * `.github/workflows/deploy.yml`: bake and build jobs on `windows-11-arm`, which is generally available for public repositories since August 2025. ## OpenVPN data channel offload OpenVPN 2.7 removed wintun, and `tap-windows6` is the only remaining alternative to `ovpn-dco`. The client had no way to provision either driver, so OpenVPN could not work on a fresh ARM64 install at all. * **New `recipes/ovpn-dco-win` 2.8.3** ships the Microsoft-attestation-signed driver byte-for-byte from the official release (win10 and win11 flavours, amd64 and arm64). I verified the SHA256 of these files against the ones inside `OpenVPN-2.7.5-I001-arm64.msi`: they are identical, so this is the same pairing upstream ships. The driver is GPLv2, so redistribution is fine; nothing is rebuilt or re-signed. * The `openvpn` recipe also packages `tapctl.exe`. Without OpenVPN's own interactive service, adapter creation has to happen on our side. * **New `DcoController`** installs the driver with `pnputil` on first use, picks the win10/win11 flavour by OS build, and creates the `AmneziaDCO` adapter with `tapctl`. `IpcServer::checkAndInstallDriver()` tries DCO first and falls back to `TapController`. * When a config is not DCO-capable (compression, `--fragment`, a proxy, a non-AEAD data cipher, `--dev tap`), openvpn falls back to `tap-windows6` on its own and dies with *"There are no TAP-Windows or ovpn-dco adapters on this system"*. That message now triggers an on-demand install of the legacy driver and one retry. * `ncp-disable` is stripped from local configs: the option was removed in OpenVPN 2.6 and 2.7 rejects it outright, so any server configured with it currently produces a hard failure on Windows. ## Dependency bumps * `openvpn` 2.7.0 → 2.7.5 — nine CVEs, and it is the userspace release that `openvpn-build` pairs with driver 2.8.3. * `tun2socks` 2.6.0 → 2.7.0. ## Notes for reviewers The commits are ordered so they can be reviewed — and if you prefer, cherry-picked — independently; only the ARM64 recipe work and the DCO work touch the same files. Qt is unchanged at 6.10.1 (`win64_msvc2022_arm64_cross_compiled` plus the same `qtremoteobjects qt5compat qtshadertools` module set the x64 job uses). `awg-windows` still has no arm64 target in its own `build.cmd`; the recipe drives the Go build directly, so nothing is blocked, but a one-line addition upstream would mirror what `wireguard-windows` already does. --- **Note on scope:** this branch also carries one commit that is *not* ARM64-specific and is submitted independently as #2882 — `WireGuardClientConfig::fromJson`/`toJson` was not round-tripping the AmneziaWG obfuscation parameters, so a WireGuard profile that had obfuscation enabled silently degrades to plain WireGuard after an upgrade. It rides along here only so the ARM64 build is self-contained; please review and merge it in #2882 — the commit is identical, so it drops out of this range once #2882 lands on `dev`. --- <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#2999
No description provided.