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