feat: per-tunnel ifname for WG service, UAPI pipe, and SCM for Windows

This commit is contained in:
cd-amn
2026-05-25 14:53:50 +00:00
parent 39f9bcfd50
commit 72147d3a67
5 changed files with 37 additions and 26 deletions
@@ -37,11 +37,14 @@ int WindowsDaemonTunnel::run(QStringList& tokens) {
QCoreApplication::setApplicationName("Amnezia VPN Tunnel");
QCoreApplication::setApplicationVersion(Constants::versionString());
if (tokens.length() != 2) {
logger.error() << "Expected 1 parameter only: the config file.";
if (tokens.length() < 2 || tokens.length() > 3) {
logger.error() << "Expected: <config> [<ifname>]";
return 1;
}
QString maybeConfig = tokens.at(1);
QString name = tokens.length() == 3 && !tokens.at(2).isEmpty()
? tokens.at(2)
: WireguardUtilsWindows::s_defaultInterfaceName();
if (!maybeConfig.startsWith("[Interface]")) {
logger.error() << "parameter Does not seem to be a config";
@@ -64,7 +67,6 @@ int WindowsDaemonTunnel::run(QStringList& tokens) {
WindowsUtils::windowsLog("Failed to get WireGuardTunnelService function");
return 1;
}
auto name = WireguardUtilsWindows::s_interfaceName();
if (!tunnelProc(maybeConfig.utf16(), name.utf16())) {
logger.error() << "Failed to activate the tunnel service";
return 1;
@@ -15,9 +15,8 @@
#include "platforms/windows/windowsutils.h"
#include "windowsdaemon.h"
#define TUNNEL_NAMED_PIPE \
"\\\\." \
"\\pipe\\ProtectedPrefix\\Administrators\\AmneziaWG\\AmneziaVPN"
#define TUNNEL_NAMED_PIPE_PREFIX \
"\\\\.\\pipe\\ProtectedPrefix\\Administrators\\AmneziaWG\\"
constexpr uint32_t WINDOWS_TUNNEL_MONITOR_TIMEOUT_MSEC = 2000;
@@ -28,6 +27,10 @@ Logger logger("WindowsTunnelService");
static bool stopAndDeleteTunnelService(SC_HANDLE service);
static bool waitForServiceStatus(SC_HANDLE service, DWORD expectedStatus);
std::wstring WindowsTunnelService::serviceNameForIfname(const QString& ifname) {
return (QStringLiteral("AmneziaWGTunnel$") + ifname).toStdWString();
}
WindowsTunnelService::WindowsTunnelService(QObject* parent) : QObject(parent) {
MZ_COUNT_CTOR(WindowsTunnelService);
logger.debug() << "WindowsTunnelService created.";
@@ -37,7 +40,7 @@ WindowsTunnelService::WindowsTunnelService(QObject* parent) : QObject(parent) {
WindowsUtils::windowsLog("Failed to open SCManager");
}
// Is the service already running? Terminate it.
// Is the legacy single-tunnel service still around? Terminate it.
SC_HANDLE service =
OpenService((SC_HANDLE)m_scm, TUNNEL_SERVICE_NAME, SERVICE_ALL_ACCESS);
if (service != nullptr) {
@@ -108,8 +111,11 @@ void WindowsTunnelService::timeout() {
emit backendFailure();
}
bool WindowsTunnelService::start(const QString& configData) {
logger.debug() << "Starting the tunnel service";
bool WindowsTunnelService::start(const QString& configData, const QString& ifname) {
logger.debug() << "Starting the tunnel service for" << ifname;
m_ifname = ifname;
const std::wstring serviceName = serviceNameForIfname(ifname);
m_logworker = new WindowsTunnelLogger(WindowsCommons::tunnelLogFile());
m_logworker->moveToThread(&m_logthread);
@@ -128,10 +134,9 @@ bool WindowsTunnelService::start(const QString& configData) {
m_logworker = nullptr;
});
// Let's see if we have to delete a previous instance.
service = OpenService(scm, TUNNEL_SERVICE_NAME, SERVICE_ALL_ACCESS);
service = OpenService(scm, serviceName.c_str(), SERVICE_ALL_ACCESS);
if (service) {
logger.debug() << "An existing service has been detected. Let's close it.";
logger.debug() << "A stale service was detected. Cleaning it up.";
if (!stopAndDeleteTunnelService(service)) {
return false;
}
@@ -143,12 +148,12 @@ bool WindowsTunnelService::start(const QString& configData) {
{
QTextStream out(&serviceCmdline);
out << "\"" << qApp->applicationFilePath() << "\" tunneldaemon \""
<< configData << "\"";
<< configData << "\" \"" << ifname << "\"";
}
logger.debug() << "Service:" << qApp->applicationFilePath();
service = CreateService(scm, TUNNEL_SERVICE_NAME, L"Amnezia VPN (tunnel)",
service = CreateService(scm, serviceName.c_str(), L"Amnezia VPN (tunnel)",
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
(const wchar_t*)serviceCmdline.utf16(), nullptr, 0,
@@ -236,8 +241,9 @@ static bool stopAndDeleteTunnelService(SC_HANDLE service) {
}
QString WindowsTunnelService::uapiCommand(const QString& command) {
// Create a pipe to the tunnel service.
LPTSTR tunnelName = (LPTSTR)TEXT(TUNNEL_NAMED_PIPE);
const std::wstring pipeName = std::wstring(TEXT(TUNNEL_NAMED_PIPE_PREFIX))
+ m_ifname.toStdWString();
LPCWSTR tunnelName = pipeName.c_str();
HANDLE pipe = CreateFile(tunnelName, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, nullptr);
if (pipe == INVALID_HANDLE_VALUE) {
@@ -9,6 +9,7 @@
#include <QObject>
#include <QThread>
#include <QTimer>
#include <string>
#include "windowstunnellogger.h"
@@ -20,11 +21,13 @@ class WindowsTunnelService final : public QObject {
WindowsTunnelService(QObject* parent = nullptr);
~WindowsTunnelService();
bool start(const QString& configData);
bool start(const QString& configData, const QString& ifname);
void stop();
bool isRunning();
QString uapiCommand(const QString& command);
static std::wstring serviceNameForIfname(const QString& ifname);
signals:
void backendFailure();
@@ -36,6 +39,7 @@ class WindowsTunnelService final : public QObject {
QTimer m_timer;
QThread m_logthread;
WindowsTunnelLogger* m_logworker = nullptr;
QString m_ifname;
// These are really SC_HANDLEs in disguise.
void* m_scm = nullptr;
@@ -12,6 +12,7 @@
#include <QFileInfo>
#include "killswitch.h"
#include "leakdetector.h"
#include "logger.h"
#include "windowsfirewall.h"
@@ -110,15 +111,14 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
configString.truncate(peerStart);
}
if (!m_tunnel.start(configString)) {
m_ifname = config.m_ifname.isEmpty() ? s_defaultInterfaceName() : config.m_ifname;
if (!m_tunnel.start(configString, m_ifname)) {
logger.error() << "Failed to activate the tunnel service";
return false;
}
// Determine the interface LUID
NET_LUID luid;
const QString ifname = config.m_ifname.isEmpty() ? interfaceName() : config.m_ifname;
DWORD result = ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(), &luid);
DWORD result = ConvertInterfaceAliasToLuid((wchar_t*)m_ifname.utf16(), &luid);
if (result != 0) {
logger.error() << "Failed to lookup LUID:" << result;
return false;
@@ -127,11 +127,11 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
m_routeMonitor = new WindowsRouteMonitor(luid.Value, this);
if (config.m_killSwitchEnabled) {
// Enable the windows firewall
NET_IFINDEX ifindex;
ConvertInterfaceLuidToIndex(&luid, &ifindex);
m_firewall->allowAllTraffic();
m_firewall->enableInterface(ifindex);
KillSwitch::instance()->addAllowedRange({});
}
logger.debug() << "Registration completed";
@@ -27,10 +27,8 @@ class WireguardUtilsWindows final : public WireguardUtils {
~WireguardUtilsWindows();
bool interfaceExists() override { return m_tunnel.isRunning(); }
QString interfaceName() override {
return WireguardUtilsWindows::s_interfaceName();
}
static const QString s_interfaceName() { return "AmneziaVPN"; }
QString interfaceName() override { return m_ifname; }
static const QString s_defaultInterfaceName() { return "AmneziaVPN"; }
bool addInterface(const InterfaceConfig& config) override;
bool deleteInterface() override;
@@ -54,6 +52,7 @@ class WireguardUtilsWindows final : public WireguardUtils {
void buildMibForwardRow(const IPAddress& prefix, void* row);
quint64 m_luid = 0;
QString m_ifname;
WindowsTunnelService m_tunnel;
QPointer<WindowsRouteMonitor> m_routeMonitor;
QPointer<WindowsFirewall> m_firewall;