fix: keep DNS and routing during seamless Xray switch on Linux

This commit is contained in:
cd-amn
2026-06-18 14:29:15 +04:00
parent 8da439f0b3
commit 8c5b4781d5
10 changed files with 61 additions and 6 deletions
+3
View File
@@ -397,6 +397,9 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
}
if ((*gateway_address) && (*interface)) {
if (QString::fromUtf8(interface).startsWith("amn")) {
continue;
}
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
break;
}
+6 -6
View File
@@ -79,12 +79,13 @@ void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSh
}
QString dns1 = vpnConfiguration.value(configKey::dns1).toString();
QString dns2 = vpnConfiguration.value(configKey::dns2).toString();
const QString xrayIfname = vpnConfiguration.value("ifname").toString();
#ifdef Q_OS_MACOS
if (!m_appSettingsRepository->isSitesSplitTunnelingEnabled() || m_appSettingsRepository->routeMode() != amnezia::RouteMode::VpnAllExceptSites) {
iface->routeAddList(protocol->vpnGateway(), QStringList() << dns1 << dns2);
iface->routeAddListVia(xrayIfname, protocol->vpnGateway(), QStringList() << dns1 << dns2);
}
#else
iface->routeAddList(protocol->vpnGateway(), QStringList() << dns1 << dns2);
iface->routeAddListVia(xrayIfname, protocol->vpnGateway(), QStringList() << dns1 << dns2);
#endif
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
@@ -99,8 +100,7 @@ void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSh
addSplitTunnelRoutes(protocolPtr->vpnGateway(), m_appSettingsRepository->routeMode());
});
} else if (m_appSettingsRepository->routeMode() == amnezia::route_mode_ns::VpnAllExceptSites) {
iface->routeAddList(protocol->vpnGateway(), QStringList() << "0.0.0.0/1");
iface->routeAddList(protocol->vpnGateway(), QStringList() << "128.0.0.0/1");
iface->routeAddListVia(xrayIfname, protocol->vpnGateway(), QStringList() << "0.0.0.0/1" << "128.0.0.0/1");
iface->routeAddList(protocol->routeGateway(), QStringList() << remoteAddress);
#ifdef Q_OS_MACOS
@@ -141,7 +141,6 @@ void VpnTrafficGuard::addSplitTunnelRoutes(const QString &gw, amnezia::RouteMode
iface->routeAddList(gw, ips);
});
// re-resolve domains
for (const QString &site : sites) {
const auto &cbResolv = [this, site, gw, mode, ips](const QHostInfo &hostInfo) {
for (const QHostAddress &addr : hostInfo.addresses()) {
@@ -238,7 +237,8 @@ void VpnTrafficGuard::applyKillSwitch(Tunnel* tunnel, const QString &gateway, co
if (isXrayBased) {
if (updatedConfig.value(configKey::splitTunnelType).toInt() == amnezia::route_mode_ns::VpnAllSites) {
static const QStringList subnets = { "1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1" };
auto routeAddList = iface->routeAddList(gateway, subnets);
const QString xrayIfname = tunnel ? tunnel->ifname() : QString();
auto routeAddList = iface->routeAddListVia(xrayIfname, gateway, subnets);
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
qCritical() << "Failed to set routes for TUN";
}
+1
View File
@@ -253,6 +253,7 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
config.insert("deviceIpv4Address", clientIp);
}
}
m_vpnConfiguration = config;
m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this);
wireTunnelSignals(m_active, /*isActive=*/true);
wireDaemonReconnectSignals();
+1
View File
@@ -10,6 +10,7 @@ class IpcInterface
// Route functions
SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
SLOT( int routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips) );
SLOT( bool clearSavedRoutes() );
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
SLOT( bool addExclusionRoute(const QString &ifname, const QString &addr) );
+9
View File
@@ -97,6 +97,15 @@ int IpcServer::routeAddList(const QString &gw, const QStringList &ips)
return Router::routeAddList(gw, ips);
}
int IpcServer::routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips)
{
#ifdef MZ_DEBUG
qDebug() << "IpcServer::routeAddListVia" << ifname;
#endif
return Router::routeAddListVia(ifname, gw, ips);
}
bool IpcServer::clearSavedRoutes()
{
#ifdef MZ_DEBUG
+1
View File
@@ -26,6 +26,7 @@ public:
virtual int createPrivilegedProcess() override;
virtual int routeAddList(const QString &gw, const QStringList &ips) override;
virtual int routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips) override;
virtual bool clearSavedRoutes() override;
virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override;
virtual bool addExclusionRoute(const QString &ifname, const QString &addr) override;
+10
View File
@@ -20,6 +20,16 @@ int Router::routeAddList(const QString &gw, const QStringList &ips)
#endif
}
int Router::routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips)
{
#ifdef Q_OS_LINUX
return RouterLinux::Instance().routeAddListVia(ifname, gw, ips);
#else
Q_UNUSED(ifname)
return routeAddList(gw, ips);
#endif
}
bool Router::clearSavedRoutes()
{
#ifdef Q_OS_WIN
+1
View File
@@ -17,6 +17,7 @@ class Router : public QObject
Q_OBJECT
public:
static int routeAddList(const QString &gw, const QStringList &ips);
static int routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips);
static bool clearSavedRoutes();
static int routeDeleteList(const QString &gw, const QStringList &ips);
static bool flushDns();
+28
View File
@@ -87,6 +87,34 @@ int RouterLinux::routeAddList(const QString &gw, const QStringList &ips)
return cnt;
}
int RouterLinux::routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips)
{
if (ifname.isEmpty()) {
qWarning() << "routeAddListVia: empty ifname";
return 0;
}
int cnt = 0;
for (const QString &ip : ips) {
QStringList args;
args << "route" << "replace" << ip;
if (!gw.isEmpty()) {
args << "via" << gw;
}
args << "dev" << ifname << "scope" << "link";
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("ip", args);
if (p.waitForFinished(2000) && p.exitCode() == 0) {
cnt++;
} else {
qWarning().noquote() << "routeAddListVia failed:" << ip << "via" << gw << "dev" << ifname
<< "rc=" << p.exitCode() << "out=" << p.readAll();
}
}
return cnt;
}
bool RouterLinux::clearSavedRoutes()
{
int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+1
View File
@@ -26,6 +26,7 @@ public:
bool routeAdd(const QString &ip, const QString &gw, const int &sock);
int routeAddList(const QString &gw, const QStringList &ips);
int routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip, const QString &gw, const int &sock);
bool routeDeleteList(const QString &gw, const QStringList &ips);