From 0dcd05c6c34fc4e2820c566d0e424758a6e6547b Mon Sep 17 00:00:00 2001 From: cd-amn Date: Mon, 11 May 2026 15:35:47 +0400 Subject: [PATCH] feat: use per-tunnel ifname instead of hardcoded WG_INTERFACE in platform helpers --- .../platforms/linux/daemon/iputilslinux.cpp | 6 +-- .../linux/daemon/linuxroutemonitor.cpp | 2 +- .../linux/daemon/wireguardutilslinux.cpp | 42 +++++++++---------- .../macos/daemon/wireguardutilsmacos.cpp | 21 ++++------ .../windows/daemon/wireguardutilswindows.cpp | 4 +- 5 files changed, 35 insertions(+), 40 deletions(-) diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index 25d4f631e..67aea5a45 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -47,7 +47,7 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { // Setup the interface to interact with struct ifreq ifr; - strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); + strncpy(ifr.ifr_name, config.m_ifname.toUtf8().constData(), IFNAMSIZ); // MTU // FIXME: We need to know how many layers deep this particular @@ -76,7 +76,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) { struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifr_addr; // Name the interface and set family - strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); + strncpy(ifr.ifr_name, config.m_ifname.toUtf8().constData(), IFNAMSIZ); ifr.ifr_addr.sa_family = AF_INET; // Get the device address to add to interface @@ -126,7 +126,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) { // Get the index of named ifr and link with ifr6 struct ifreq ifr; - strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); + strncpy(ifr.ifr_name, config.m_ifname.toUtf8().constData(), IFNAMSIZ); ifr.ifr_addr.sa_family = AF_INET6; int ret = ioctl(sockfd, SIOGIFINDEX, &ifr); if (ret) { diff --git a/client/platforms/linux/daemon/linuxroutemonitor.cpp b/client/platforms/linux/daemon/linuxroutemonitor.cpp index f943524df..c3fdb15e3 100644 --- a/client/platforms/linux/daemon/linuxroutemonitor.cpp +++ b/client/platforms/linux/daemon/linuxroutemonitor.cpp @@ -153,7 +153,7 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type, } if (rtm->rtm_type == RTN_UNICAST) { - int index = if_nametoindex(WG_INTERFACE); + int index = if_nametoindex(m_ifname.toUtf8().constData()); if (index <= 0) { logger.error() << "if_nametoindex() failed:" << strerror(errno); diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 15c6edccb..9b8e377f7 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -59,19 +60,20 @@ void WireguardUtilsLinux::tunnelErrorOccurred(QProcess::ProcessError error) { } bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { - Q_UNUSED(config); if (m_tunnel.state() != QProcess::NotRunning) { logger.warning() << "Unable to start: tunnel process already running"; return false; } + const QString ifname = config.m_ifname.isEmpty() ? QString(WG_INTERFACE) : config.m_ifname; + QDir wgRuntimeDir(WG_RUNTIME_DIR); if (!wgRuntimeDir.exists()) { wgRuntimeDir.mkpath("."); } QProcessEnvironment pe = QProcessEnvironment::systemEnvironment(); - QString wgNameFile = wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".sock"); + QString wgNameFile = wgRuntimeDir.filePath(ifname + ".sock"); pe.insert("WG_TUN_NAME_FILE", wgNameFile); #ifdef MZ_DEBUG pe.insert("LOG_LEVEL", "debug"); @@ -79,7 +81,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { m_tunnel.setProcessEnvironment(pe); QDir appPath(QCoreApplication::applicationDirPath()); - QStringList wgArgs = {"-f", "amn0"}; + QStringList wgArgs = {"-f", ifname}; m_tunnel.start(appPath.filePath("amneziawg-go"), wgArgs); if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) { logger.error() << "Unable to start tunnel process due to timeout"; @@ -194,7 +196,7 @@ bool WireguardUtilsLinux::deleteInterface() { // Garbage collect. QDir wgRuntimeDir(WG_RUNTIME_DIR); - QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); + QFile::remove(wgRuntimeDir.filePath(m_ifname + ".name")); // double-check + ensure our firewall is installed and enabled KillSwitch::instance()->disableKillSwitch(); @@ -237,8 +239,10 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) { // Exclude the server address, except for multihop exit servers. if ((config.m_hopType != InterfaceConfig::MultiHopExit) && (m_rtmonitor != nullptr)) { - m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); - m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); + if (!config.m_serverIpv4AddrIn.isEmpty()) + m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); + if (!config.m_serverIpv6AddrIn.isEmpty()) + m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); } int err = uapiErrno(uapiCommand(message)); @@ -255,8 +259,10 @@ bool WireguardUtilsLinux::deletePeer(const InterfaceConfig& config) { // Clear exclustion routes for this peer. if ((config.m_hopType != InterfaceConfig::MultiHopExit) && (m_rtmonitor != nullptr)) { - m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); - m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); + if (!config.m_serverIpv4AddrIn.isEmpty()) + m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); + if (!config.m_serverIpv6AddrIn.isEmpty()) + m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); } QString message; @@ -389,13 +395,9 @@ bool WireguardUtilsLinux::excludeLocalNetworks(const QList& routes) { QString WireguardUtilsLinux::uapiCommand(const QString& command) { QLocalSocket socket; - QTimer uapiTimeout; QDir wgRuntimeDir(WG_RUNTIME_DIR); QString wgSocketFile = wgRuntimeDir.filePath(m_ifname + ".sock"); - uapiTimeout.setSingleShot(true); - uapiTimeout.start(WG_TUN_PROC_TIMEOUT); - socket.connectToServer(wgSocketFile, QIODevice::ReadWrite); if (!socket.waitForConnected(WG_TUN_PROC_TIMEOUT)) { logger.error() << "QLocalSocket::waitForConnected() failed:" @@ -410,13 +412,15 @@ QString WireguardUtilsLinux::uapiCommand(const QString& command) { } socket.write(message); + QElapsedTimer elapsed; + elapsed.start(); QByteArray reply; while (!reply.contains("\n\n")) { - if (!uapiTimeout.isActive()) { + const qint64 remaining = WG_TUN_PROC_TIMEOUT - elapsed.elapsed(); + if (remaining <= 0 || !socket.waitForReadyRead(static_cast(remaining))) { logger.error() << "UAPI command timed out"; return QString(); } - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); reply.append(socket.readAll()); } @@ -442,19 +446,13 @@ QString WireguardUtilsLinux::waitForTunnelName(const QString& filename) { timeout.setSingleShot(true); timeout.start(WG_TUN_PROC_TIMEOUT); - QFile file(filename); - while ((m_tunnel.state() == QProcess::Running) && timeout.isActive()) { QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - QString ifname = "amn0"; - // Test-connect to the UAPI socket. QLocalSocket sock; - QDir wgRuntimeDir(WG_RUNTIME_DIR); - QString sockName = wgRuntimeDir.filePath(ifname + ".sock"); - sock.connectToServer(sockName, QIODevice::ReadWrite); + sock.connectToServer(filename, QIODevice::ReadWrite); if (sock.waitForConnected(100)) { - return ifname; + return QFileInfo(filename).baseName(); } } diff --git a/client/platforms/macos/daemon/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index 69367e312..e12fc1c38 100644 --- a/client/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/client/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -58,19 +59,20 @@ void WireguardUtilsMacos::tunnelErrorOccurred(QProcess::ProcessError error) { } bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) { - Q_UNUSED(config); if (m_tunnel.state() != QProcess::NotRunning) { logger.warning() << "Unable to start: tunnel process already running"; return false; } + const QString ifname = config.m_ifname.isEmpty() ? QString(WG_INTERFACE) : config.m_ifname; + QDir wgRuntimeDir(WG_RUNTIME_DIR); if (!wgRuntimeDir.exists()) { wgRuntimeDir.mkpath("."); } QProcessEnvironment pe = QProcessEnvironment::systemEnvironment(); - QString wgNameFile = wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"); + QString wgNameFile = wgRuntimeDir.filePath(ifname + ".name"); pe.insert("WG_TUN_NAME_FILE", wgNameFile); #ifdef MZ_DEBUG pe.insert("LOG_LEVEL", "debug"); @@ -92,6 +94,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) { m_tunnel.kill(); return false; } + QFile::remove(wgNameFile); logger.debug() << "Created wireguard interface" << m_ifname; // Start the routing table monitor. @@ -190,10 +193,6 @@ bool WireguardUtilsMacos::deleteInterface() { m_tunnel.waitForFinished(WG_TUN_PROC_TIMEOUT); } - // Garbage collect. - QDir wgRuntimeDir(WG_RUNTIME_DIR); - QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); - // double-check + ensure our firewall is installed and enabled KillSwitch::instance()->disableKillSwitch(); @@ -389,13 +388,9 @@ bool WireguardUtilsMacos::excludeLocalNetworks(const QList& routes) { QString WireguardUtilsMacos::uapiCommand(const QString& command) { QLocalSocket socket; - QTimer uapiTimeout; QDir wgRuntimeDir(WG_RUNTIME_DIR); QString wgSocketFile = wgRuntimeDir.filePath(m_ifname + ".sock"); - uapiTimeout.setSingleShot(true); - uapiTimeout.start(WG_TUN_PROC_TIMEOUT); - socket.connectToServer(wgSocketFile, QIODevice::ReadWrite); if (!socket.waitForConnected(WG_TUN_PROC_TIMEOUT)) { logger.error() << "QLocalSocket::waitForConnected() failed:" @@ -410,13 +405,15 @@ QString WireguardUtilsMacos::uapiCommand(const QString& command) { } socket.write(message); + QElapsedTimer elapsed; + elapsed.start(); QByteArray reply; while (!reply.contains("\n\n")) { - if (!uapiTimeout.isActive()) { + const qint64 remaining = WG_TUN_PROC_TIMEOUT - elapsed.elapsed(); + if (remaining <= 0 || !socket.waitForReadyRead(static_cast(remaining))) { logger.error() << "UAPI command timed out"; return QString(); } - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); reply.append(socket.readAll()); } diff --git a/client/platforms/windows/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index a5c9c84d1..c307fae41 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -117,8 +117,8 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) { // Determine the interface LUID NET_LUID luid; - QString ifAlias = interfaceName(); - DWORD result = ConvertInterfaceAliasToLuid((wchar_t*)ifAlias.utf16(), &luid); + const QString ifname = config.m_ifname.isEmpty() ? interfaceName() : config.m_ifname; + DWORD result = ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(), &luid); if (result != 0) { logger.error() << "Failed to lookup LUID:" << result; return false;