From 97b2de8cd13f074f416478002667e2c50cfc0790 Mon Sep 17 00:00:00 2001 From: cd-amn Date: Tue, 16 Jun 2026 12:00:37 +0000 Subject: [PATCH] refactor: TrafficGuard owns adapter IP swap for WG/AWG and Xray --- client/core/vpnTrafficGuard.cpp | 33 +++++++----- client/daemon/daemon.cpp | 8 --- client/daemon/wireguardutils.h | 3 -- .../windows/daemon/wireguardutilswindows.cpp | 54 ------------------- .../windows/daemon/wireguardutilswindows.h | 3 -- client/vpnConnection.cpp | 14 +++++ 6 files changed, 34 insertions(+), 81 deletions(-) diff --git a/client/core/vpnTrafficGuard.cpp b/client/core/vpnTrafficGuard.cpp index a43e0f144..bf4a701da 100644 --- a/client/core/vpnTrafficGuard.cpp +++ b/client/core/vpnTrafficGuard.cpp @@ -457,20 +457,11 @@ void VpnTrafficGuard::commit(Tunnel* tunnel) { if (!tunnel) return; -#if defined(AMNEZIA_DESKTOP) && defined(Q_OS_WIN) - if (VpnProtocol::isXrayBased(tunnel->container())) { - const QJsonObject &cfg = tunnel->config(); - const QString ipv4 = cfg.value(QStringLiteral("deviceIpv4Address")).toString(); - const QString ipv6 = cfg.value(QStringLiteral("deviceIpv6Address")).toString(); - const QString handover = tunnel->handoverIfname(); +#ifdef Q_OS_WIN + const QString ipv4 = tunnel->config().value(QStringLiteral("deviceIpv4Address")).toString(); + const QString ipv6 = tunnel->config().value(QStringLiteral("deviceIpv6Address")).toString(); + if (!ipv4.isEmpty() || !ipv6.isEmpty()) { IpcClient::withInterface([&](QSharedPointer iface) { - if (!handover.isEmpty() && handover != tunnel->ifname()) { - auto rm = iface->removeAdapterAddress(handover, ipv4, ipv6); - if (!rm.waitForFinished(2000) || !rm.returnValue()) { - qWarning() << "VpnTrafficGuard::commit: removeAdapterAddress failed for" - << handover; - } - } auto ap = iface->applyAdapterAddress(tunnel->ifname(), ipv4, ipv6); if (!ap.waitForFinished(15000) || !ap.returnValue()) { qWarning() << "VpnTrafficGuard::commit: applyAdapterAddress failed for" @@ -510,6 +501,22 @@ void VpnTrafficGuard::swap(Tunnel* from, Tunnel* to) to->setHandoverIfname(from->ifname()); } +#ifdef Q_OS_WIN + if (from) { + const QString fromIpv4 = from->config().value(QStringLiteral("deviceIpv4Address")).toString(); + const QString fromIpv6 = from->config().value(QStringLiteral("deviceIpv6Address")).toString(); + if (!fromIpv4.isEmpty() || !fromIpv6.isEmpty()) { + IpcClient::withInterface([&](QSharedPointer iface) { + auto rm = iface->removeAdapterAddress(from->ifname(), fromIpv4, fromIpv6); + if (!rm.waitForFinished(2000) || !rm.returnValue()) { + qWarning() << "VpnTrafficGuard::swap: removeAdapterAddress failed for" + << from->ifname(); + } + }); + } + } +#endif + QEventLoop loop; QTimer guard; guard.setSingleShot(true); diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index 21d81baf9..d0e47eb51 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -128,14 +128,6 @@ bool Daemon::setPrimary(const QString& ifname, const InterfaceConfig& config) { const QString priorPrimary = m_primaryIfname; m_primaryIfname = ifname; - if (!priorPrimary.isEmpty() && priorPrimary != ifname) { - if (WireguardUtils* oldWg = m_tunnels.value(priorPrimary)) { - const InterfaceConfig& oldConfig = m_connections.value(priorPrimary).m_config; - oldWg->removeDeviceAddresses(oldConfig.m_deviceIpv4Address, oldConfig.m_deviceIpv6Address); - } - } - wg->applyDeviceAddresses(config.m_deviceIpv4Address, config.m_deviceIpv6Address); - auto failure_guard = qScopeGuard([this, ifname, priorPrimary] { deactivateTunnel(ifname); m_primaryIfname = priorPrimary; diff --git a/client/daemon/wireguardutils.h b/client/daemon/wireguardutils.h index 1e893c321..3f927c3ab 100644 --- a/client/daemon/wireguardutils.h +++ b/client/daemon/wireguardutils.h @@ -37,9 +37,6 @@ class WireguardUtils : public QObject { virtual bool addInterface(const InterfaceConfig& config) = 0; virtual bool deleteInterface() = 0; - virtual bool applyDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) { return true; } - virtual bool removeDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) { return true; } - virtual bool updatePeer(const InterfaceConfig& config) = 0; virtual bool deletePeer(const InterfaceConfig& config) = 0; virtual QList getPeerStatus() = 0; diff --git a/client/platforms/windows/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index 10eba5ad9..5a14a4147 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -152,60 +152,6 @@ bool WireguardUtilsWindows::deleteInterface() { return true; } -bool WireguardUtilsWindows::applyDeviceAddresses(const QString& ipv4Address, - const QString& ipv6Address) { - auto applyOne = [&](const QString& addr, int family, uint8_t prefix) -> bool { - if (addr.isEmpty()) return true; - const QByteArray ip = addr.section('/', 0, 0).toUtf8(); - MIB_UNICASTIPADDRESS_ROW row; - InitializeUnicastIpAddressEntry(&row); - row.InterfaceLuid.Value = m_luid; - row.Address.si_family = static_cast(family); - row.OnLinkPrefixLength = prefix; - row.DadState = IpDadStatePreferred; - void* dst = (family == AF_INET) - ? static_cast(&row.Address.Ipv4.sin_addr) - : static_cast(&row.Address.Ipv6.sin6_addr); - if (InetPtonA(family, ip.constData(), dst) != 1) { - logger.error() << "applyDeviceAddresses: cannot parse" << addr; - return false; - } - DWORD r = CreateUnicastIpAddressEntry(&row); - logger.debug() << "Apply" << addr << "to" << m_ifname << "result:" << r; - return r == NO_ERROR || r == ERROR_OBJECT_ALREADY_EXISTS; - }; - - if (!applyOne(ipv4Address, AF_INET, 32)) return false; - if (!applyOne(ipv6Address, AF_INET6, 128)) return false; - return true; -} - -bool WireguardUtilsWindows::removeDeviceAddresses(const QString& ipv4Address, - const QString& ipv6Address) { - auto removeOne = [&](const QString& addr, int family) -> bool { - if (addr.isEmpty()) return true; - const QByteArray ip = addr.section('/', 0, 0).toUtf8(); - MIB_UNICASTIPADDRESS_ROW row; - InitializeUnicastIpAddressEntry(&row); - row.InterfaceLuid.Value = m_luid; - row.Address.si_family = static_cast(family); - void* dst = (family == AF_INET) - ? static_cast(&row.Address.Ipv4.sin_addr) - : static_cast(&row.Address.Ipv6.sin6_addr); - if (InetPtonA(family, ip.constData(), dst) != 1) { - logger.error() << "removeDeviceAddresses: cannot parse" << addr; - return false; - } - DWORD r = DeleteUnicastIpAddressEntry(&row); - logger.debug() << "Remove" << addr << "from" << m_ifname << "result:" << r; - return r == NO_ERROR || r == ERROR_NOT_FOUND; - }; - - bool ok = removeOne(ipv4Address, AF_INET); - ok &= removeOne(ipv6Address, AF_INET6); - return ok; -} - bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) { QByteArray publicKey = QByteArray::fromBase64(qPrintable(config.m_serverPublicKey)); diff --git a/client/platforms/windows/daemon/wireguardutilswindows.h b/client/platforms/windows/daemon/wireguardutilswindows.h index f1931522b..8828cb82f 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.h +++ b/client/platforms/windows/daemon/wireguardutilswindows.h @@ -32,9 +32,6 @@ class WireguardUtilsWindows final : public WireguardUtils { bool addInterface(const InterfaceConfig& config) override; bool deleteInterface() override; - bool applyDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) override; - bool removeDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) override; - bool updatePeer(const InterfaceConfig& config) override; bool deletePeer(const InterfaceConfig& config) override; QList getPeerStatus() override; diff --git a/client/vpnConnection.cpp b/client/vpnConnection.cpp index 0726393f6..41cf7ea6c 100644 --- a/client/vpnConnection.cpp +++ b/client/vpnConnection.cpp @@ -224,6 +224,13 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai if (isXray) { config.insert("tunName", preAllocatedIfname); config.insert("deviceIpv4Address", amnezia::protocols::xray::defaultLocalAddr); + } else if (isWg) { + const QString protoName = config.value("protocol").toString(); + const QJsonObject wgConfig = config.value(protoName + "_config_data").toObject(); + const QString clientIp = wgConfig.value(amnezia::configKey::clientIp).toString(); + if (!clientIp.isEmpty()) { + config.insert("deviceIpv4Address", clientIp); + } } m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this); wireTunnelSignals(m_active, /*isActive=*/true); @@ -553,6 +560,13 @@ void VpnConnection::startTunnelSwitch(DockerContainer container, if (VpnProtocol::isXrayBased(container)) { config.insert("tunName", stagingIfname); config.insert("deviceIpv4Address", amnezia::protocols::xray::defaultLocalAddr); + } else if (VpnProtocol::isWireGuardBased(container)) { + const QString protoName = config.value("protocol").toString(); + const QJsonObject wgConfig = config.value(protoName + "_config_data").toObject(); + const QString clientIp = wgConfig.value(amnezia::configKey::clientIp).toString(); + if (!clientIp.isEmpty()) { + config.insert("deviceIpv4Address", clientIp); + } } appendKillSwitchConfig(config); appendSplitTunnelingConfig(config);