mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
feat: route Xray through Tunnel for seamless server switch
This commit is contained in:
@@ -113,6 +113,12 @@ bool VpnProtocol::isWireGuardBased(amnezia::DockerContainer container)
|
||||
|| container == amnezia::DockerContainer::WireGuard;
|
||||
}
|
||||
|
||||
bool VpnProtocol::isXrayBased(amnezia::DockerContainer container)
|
||||
{
|
||||
return container == amnezia::DockerContainer::Xray
|
||||
|| container == amnezia::DockerContainer::SSXray;
|
||||
}
|
||||
|
||||
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration)
|
||||
{
|
||||
switch (container) {
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
|
||||
static VpnProtocol* factory(amnezia::DockerContainer container, const QJsonObject &configuration);
|
||||
static bool isWireGuardBased(amnezia::DockerContainer container);
|
||||
static bool isXrayBased(amnezia::DockerContainer container);
|
||||
|
||||
signals:
|
||||
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
|
||||
|
||||
@@ -28,11 +28,17 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
|
||||
m_routeMode = static_cast<amnezia::RouteMode>(configuration.value(amnezia::configKey::splitTunnelType).toInt());
|
||||
m_remoteAddress = NetworkUtilities::getIPAddress(m_rawConfig.value(amnezia::configKey::hostName).toString());
|
||||
|
||||
m_tunName = configuration.value("tunName").toString();
|
||||
if (m_tunName.isEmpty()) {
|
||||
m_tunName = configuration.value("ifname").toString();
|
||||
}
|
||||
if (m_tunName.isEmpty()) {
|
||||
#ifdef Q_OS_MACOS
|
||||
m_tunName = configuration.value("tunName").toString("utun22");
|
||||
m_tunName = QStringLiteral("utun22");
|
||||
#else
|
||||
m_tunName = configuration.value("tunName").toString("tun2");
|
||||
m_tunName = QStringLiteral("tun2");
|
||||
#endif
|
||||
}
|
||||
const QString primaryDns = configuration.value(amnezia::configKey::dns1).toString();
|
||||
m_dnsServers.push_back(QHostAddress(primaryDns));
|
||||
if (primaryDns != amnezia::protocols::dns::amneziaDnsIp) {
|
||||
@@ -167,7 +173,9 @@ void XrayProtocol::stop()
|
||||
void XrayProtocol::setPrimary(const QJsonObject &config)
|
||||
{
|
||||
Q_UNUSED(config)
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
emit primaryReady();
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
ErrorCode XrayProtocol::startTun2Socks()
|
||||
|
||||
@@ -167,12 +167,13 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
const bool isWg = VpnProtocol::isWireGuardBased(container);
|
||||
const QString preAllocatedIfname = isWg ? allocateIfname() : QString();
|
||||
const bool isXray = VpnProtocol::isXrayBased(container);
|
||||
const bool useTunnelPath = isWg || isXray;
|
||||
const QString preAllocatedIfname = useTunnelPath ? allocateIfname() : QString();
|
||||
|
||||
// Seamless WG -> WG switch path: already connected via Tunnel, new container is also WG.
|
||||
if (m_active
|
||||
&& m_connectionState == Vpn::ConnectionState::Connected
|
||||
&& isWg) {
|
||||
&& useTunnelPath) {
|
||||
if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) {
|
||||
releaseIfname(preAllocatedIfname);
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
@@ -184,7 +185,7 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
}
|
||||
|
||||
if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) {
|
||||
if (isWg) releaseIfname(preAllocatedIfname);
|
||||
if (useTunnelPath) releaseIfname(preAllocatedIfname);
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed);
|
||||
return;
|
||||
@@ -217,8 +218,11 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
m_remoteAddress = resolvedRemote;
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (isWg) {
|
||||
if (useTunnelPath) {
|
||||
config.insert("ifname", preAllocatedIfname);
|
||||
if (isXray) {
|
||||
config.insert("tunName", preAllocatedIfname);
|
||||
}
|
||||
m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this);
|
||||
wireTunnelSignals(m_active, /*isActive=*/true);
|
||||
wireDaemonReconnectSignals();
|
||||
@@ -544,6 +548,9 @@ void VpnConnection::startTunnelSwitch(DockerContainer container,
|
||||
{
|
||||
QJsonObject config = vpnConfiguration;
|
||||
config.insert("ifname", stagingIfname);
|
||||
if (VpnProtocol::isXrayBased(container)) {
|
||||
config.insert("tunName", stagingIfname);
|
||||
}
|
||||
appendKillSwitchConfig(config);
|
||||
appendSplitTunnelingConfig(config);
|
||||
|
||||
@@ -560,10 +567,8 @@ void VpnConnection::onTunnelPrepared()
|
||||
if (!tunnel) return;
|
||||
|
||||
if (tunnel == m_staging && m_active) {
|
||||
const QString oldIfname = m_active->ifname();
|
||||
m_trafficGuard->swap(m_active, m_staging);
|
||||
delete m_active;
|
||||
releaseIfname(oldIfname);
|
||||
Tunnel* oldTunnel = m_active;
|
||||
const QString oldIfname = oldTunnel->ifname();
|
||||
|
||||
m_active = m_staging;
|
||||
m_staging = nullptr;
|
||||
@@ -571,6 +576,10 @@ void VpnConnection::onTunnelPrepared()
|
||||
m_vpnConfiguration = m_active->config();
|
||||
m_remoteAddress = m_active->remoteAddress();
|
||||
m_trafficGuard->setConfig(m_vpnConfiguration);
|
||||
|
||||
m_trafficGuard->swap(oldTunnel, m_active);
|
||||
delete oldTunnel;
|
||||
releaseIfname(oldIfname);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user