mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
feat: TrafficGuard owns Xray adapter IP swap on Windows
This commit is contained in:
@@ -272,11 +272,15 @@ ErrorCode XrayProtocol::setupRouting()
|
|||||||
{
|
{
|
||||||
return IpcClient::withInterface(
|
return IpcClient::withInterface(
|
||||||
[this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
[this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
auto createTun = iface->createTun(m_tunName, amnezia::protocols::xray::defaultLocalAddr);
|
auto createTun = iface->createTun(m_tunName, amnezia::protocols::xray::defaultLocalAddr);
|
||||||
if (!createTun.waitForFinished() || !createTun.returnValue()) {
|
if (!createTun.waitForFinished() || !createTun.returnValue()) {
|
||||||
qCritical() << "Failed to assign IP address for TUN";
|
qCritical() << "Failed to assign IP address for TUN";
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(iface)
|
||||||
|
#endif
|
||||||
|
|
||||||
emit tunnelAddressesUpdated(m_vpnGateway, m_vpnLocalAddress);
|
emit tunnelAddressesUpdated(m_vpnGateway, m_vpnLocalAddress);
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
|
|||||||
@@ -456,6 +456,30 @@ void VpnTrafficGuard::bringUp(Tunnel* tunnel)
|
|||||||
void VpnTrafficGuard::commit(Tunnel* tunnel)
|
void VpnTrafficGuard::commit(Tunnel* tunnel)
|
||||||
{
|
{
|
||||||
if (!tunnel) return;
|
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();
|
||||||
|
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> 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"
|
||||||
|
<< tunnel->ifname();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
applyPolicy(tunnel);
|
applyPolicy(tunnel);
|
||||||
connect(tunnel, &Tunnel::activated, this, [this, tunnel] {
|
connect(tunnel, &Tunnel::activated, this, [this, tunnel] {
|
||||||
if (auto p = tunnel->protocol()) {
|
if (auto p = tunnel->protocol()) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
#include "core/utils/networkUtilities.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
|
|
||||||
using namespace ProtocolUtils;
|
using namespace ProtocolUtils;
|
||||||
@@ -222,6 +223,7 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
|||||||
config.insert("ifname", preAllocatedIfname);
|
config.insert("ifname", preAllocatedIfname);
|
||||||
if (isXray) {
|
if (isXray) {
|
||||||
config.insert("tunName", preAllocatedIfname);
|
config.insert("tunName", preAllocatedIfname);
|
||||||
|
config.insert("deviceIpv4Address", amnezia::protocols::xray::defaultLocalAddr);
|
||||||
}
|
}
|
||||||
m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this);
|
m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this);
|
||||||
wireTunnelSignals(m_active, /*isActive=*/true);
|
wireTunnelSignals(m_active, /*isActive=*/true);
|
||||||
@@ -550,11 +552,15 @@ void VpnConnection::startTunnelSwitch(DockerContainer container,
|
|||||||
config.insert("ifname", stagingIfname);
|
config.insert("ifname", stagingIfname);
|
||||||
if (VpnProtocol::isXrayBased(container)) {
|
if (VpnProtocol::isXrayBased(container)) {
|
||||||
config.insert("tunName", stagingIfname);
|
config.insert("tunName", stagingIfname);
|
||||||
|
config.insert("deviceIpv4Address", amnezia::protocols::xray::defaultLocalAddr);
|
||||||
}
|
}
|
||||||
appendKillSwitchConfig(config);
|
appendKillSwitchConfig(config);
|
||||||
appendSplitTunnelingConfig(config);
|
appendSplitTunnelingConfig(config);
|
||||||
|
|
||||||
m_staging = new Tunnel(stagingIfname, container, config, resolvedRemote, this);
|
m_staging = new Tunnel(stagingIfname, container, config, resolvedRemote, this);
|
||||||
|
if (m_active) {
|
||||||
|
m_staging->setHandoverIfname(m_active->ifname());
|
||||||
|
}
|
||||||
wireTunnelSignals(m_staging, /*isActive=*/false);
|
wireTunnelSignals(m_staging, /*isActive=*/false);
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Switching);
|
setConnectionState(Vpn::ConnectionState::Switching);
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ class IpcInterface
|
|||||||
SLOT( bool createTun(const QString &dev, const QString &subnet) );
|
SLOT( bool createTun(const QString &dev, const QString &subnet) );
|
||||||
SLOT( bool deleteTun(const QString &dev) );
|
SLOT( bool deleteTun(const QString &dev) );
|
||||||
|
|
||||||
|
SLOT( bool applyAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) );
|
||||||
|
|
||||||
|
SLOT( bool removeAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) );
|
||||||
|
|
||||||
SLOT( bool StartRoutingIpv6() );
|
SLOT( bool StartRoutingIpv6() );
|
||||||
SLOT( bool StopRoutingIpv6() );
|
SLOT( bool StopRoutingIpv6() );
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
#include "tapcontroller_win.h"
|
#include "tapcontroller_win.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -212,6 +215,79 @@ bool IpcServer::deleteTun(const QString &dev)
|
|||||||
return Router::deleteTun(dev);
|
return Router::deleteTun(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IpcServer::applyAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
bool ok = true;
|
||||||
|
// Router::createTun on Windows assigns the address and blocks until it
|
||||||
|
// becomes live on the adapter (NotifyUnicastIpAddressChange callback).
|
||||||
|
if (!ipv4.isEmpty()) {
|
||||||
|
ok &= Router::createTun(ifname, ipv4);
|
||||||
|
}
|
||||||
|
if (!ipv6.isEmpty()) {
|
||||||
|
NET_LUID luid;
|
||||||
|
if (ConvertInterfaceAliasToLuid(reinterpret_cast<const wchar_t*>(ifname.utf16()), &luid) != NO_ERROR) {
|
||||||
|
qWarning() << "IpcServer::applyAdapterAddress: cannot resolve" << ifname;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const QByteArray ip = ipv6.section('/', 0, 0).toUtf8();
|
||||||
|
MIB_UNICASTIPADDRESS_ROW row;
|
||||||
|
InitializeUnicastIpAddressEntry(&row);
|
||||||
|
row.InterfaceLuid.Value = luid.Value;
|
||||||
|
row.Address.si_family = AF_INET6;
|
||||||
|
row.OnLinkPrefixLength = 128;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
if (InetPtonA(AF_INET6, ip.toStdString().c_str(), &row.Address.Ipv6.sin6_addr) != 1) {
|
||||||
|
qWarning() << "IpcServer::applyAdapterAddress: cannot parse" << ipv6;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD r = CreateUnicastIpAddressEntry(&row);
|
||||||
|
ok &= (r == NO_ERROR || r == ERROR_OBJECT_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(ifname)
|
||||||
|
Q_UNUSED(ipv4)
|
||||||
|
Q_UNUSED(ipv6)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcServer::removeAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
NET_LUID luid;
|
||||||
|
if (ConvertInterfaceAliasToLuid(reinterpret_cast<const wchar_t*>(ifname.utf16()), &luid) != NO_ERROR) {
|
||||||
|
qWarning() << "IpcServer::removeAdapterAddress: cannot resolve" << ifname;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = luid.Value;
|
||||||
|
row.Address.si_family = static_cast<ADDRESS_FAMILY>(family);
|
||||||
|
void* dst = (family == AF_INET)
|
||||||
|
? static_cast<void*>(&row.Address.Ipv4.sin_addr)
|
||||||
|
: static_cast<void*>(&row.Address.Ipv6.sin6_addr);
|
||||||
|
if (InetPtonA(family, ip.toStdString().c_str(), dst) != 1) return false;
|
||||||
|
DWORD r = DeleteUnicastIpAddressEntry(&row);
|
||||||
|
return r == NO_ERROR || r == ERROR_NOT_FOUND;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ok = removeOne(ipv4, AF_INET);
|
||||||
|
ok &= removeOne(ipv6, AF_INET6);
|
||||||
|
return ok;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(ifname)
|
||||||
|
Q_UNUSED(ipv4)
|
||||||
|
Q_UNUSED(ipv6)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool IpcServer::updateResolvers(const QString &ifname, const QList<QHostAddress> &resolvers)
|
bool IpcServer::updateResolvers(const QString &ifname, const QList<QHostAddress> &resolvers)
|
||||||
{
|
{
|
||||||
#ifdef MZ_DEBUG
|
#ifdef MZ_DEBUG
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public:
|
|||||||
virtual void setLogsEnabled(bool enabled) override;
|
virtual void setLogsEnabled(bool enabled) override;
|
||||||
virtual bool createTun(const QString &dev, const QString &subnet) override;
|
virtual bool createTun(const QString &dev, const QString &subnet) override;
|
||||||
virtual bool deleteTun(const QString &dev) override;
|
virtual bool deleteTun(const QString &dev) override;
|
||||||
|
virtual bool applyAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) override;
|
||||||
|
virtual bool removeAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) override;
|
||||||
virtual bool StartRoutingIpv6() override;
|
virtual bool StartRoutingIpv6() override;
|
||||||
virtual bool StopRoutingIpv6() override;
|
virtual bool StopRoutingIpv6() override;
|
||||||
virtual bool disableAllTraffic() override;
|
virtual bool disableAllTraffic() override;
|
||||||
|
|||||||
Reference in New Issue
Block a user