mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
fix: keep DNS and routing during seamless Xray switch on Linux
This commit is contained in:
@@ -397,6 +397,9 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((*gateway_address) && (*interface)) {
|
if ((*gateway_address) && (*interface)) {
|
||||||
|
if (QString::fromUtf8(interface).startsWith("amn")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
|
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,12 +79,13 @@ void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSh
|
|||||||
}
|
}
|
||||||
QString dns1 = vpnConfiguration.value(configKey::dns1).toString();
|
QString dns1 = vpnConfiguration.value(configKey::dns1).toString();
|
||||||
QString dns2 = vpnConfiguration.value(configKey::dns2).toString();
|
QString dns2 = vpnConfiguration.value(configKey::dns2).toString();
|
||||||
|
const QString xrayIfname = vpnConfiguration.value("ifname").toString();
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
if (!m_appSettingsRepository->isSitesSplitTunnelingEnabled() || m_appSettingsRepository->routeMode() != amnezia::RouteMode::VpnAllExceptSites) {
|
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
|
#else
|
||||||
iface->routeAddList(protocol->vpnGateway(), QStringList() << dns1 << dns2);
|
iface->routeAddListVia(xrayIfname, protocol->vpnGateway(), QStringList() << dns1 << dns2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
|
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
|
||||||
@@ -99,8 +100,7 @@ void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSh
|
|||||||
addSplitTunnelRoutes(protocolPtr->vpnGateway(), m_appSettingsRepository->routeMode());
|
addSplitTunnelRoutes(protocolPtr->vpnGateway(), m_appSettingsRepository->routeMode());
|
||||||
});
|
});
|
||||||
} else if (m_appSettingsRepository->routeMode() == amnezia::route_mode_ns::VpnAllExceptSites) {
|
} else if (m_appSettingsRepository->routeMode() == amnezia::route_mode_ns::VpnAllExceptSites) {
|
||||||
iface->routeAddList(protocol->vpnGateway(), QStringList() << "0.0.0.0/1");
|
iface->routeAddListVia(xrayIfname, protocol->vpnGateway(), QStringList() << "0.0.0.0/1" << "128.0.0.0/1");
|
||||||
iface->routeAddList(protocol->vpnGateway(), QStringList() << "128.0.0.0/1");
|
|
||||||
|
|
||||||
iface->routeAddList(protocol->routeGateway(), QStringList() << remoteAddress);
|
iface->routeAddList(protocol->routeGateway(), QStringList() << remoteAddress);
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
@@ -141,7 +141,6 @@ void VpnTrafficGuard::addSplitTunnelRoutes(const QString &gw, amnezia::RouteMode
|
|||||||
iface->routeAddList(gw, ips);
|
iface->routeAddList(gw, ips);
|
||||||
});
|
});
|
||||||
|
|
||||||
// re-resolve domains
|
|
||||||
for (const QString &site : sites) {
|
for (const QString &site : sites) {
|
||||||
const auto &cbResolv = [this, site, gw, mode, ips](const QHostInfo &hostInfo) {
|
const auto &cbResolv = [this, site, gw, mode, ips](const QHostInfo &hostInfo) {
|
||||||
for (const QHostAddress &addr : hostInfo.addresses()) {
|
for (const QHostAddress &addr : hostInfo.addresses()) {
|
||||||
@@ -238,7 +237,8 @@ void VpnTrafficGuard::applyKillSwitch(Tunnel* tunnel, const QString &gateway, co
|
|||||||
if (isXrayBased) {
|
if (isXrayBased) {
|
||||||
if (updatedConfig.value(configKey::splitTunnelType).toInt() == amnezia::route_mode_ns::VpnAllSites) {
|
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" };
|
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()) {
|
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
|
||||||
qCritical() << "Failed to set routes for TUN";
|
qCritical() << "Failed to set routes for TUN";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
|||||||
config.insert("deviceIpv4Address", clientIp);
|
config.insert("deviceIpv4Address", clientIp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_vpnConfiguration = config;
|
||||||
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);
|
||||||
wireDaemonReconnectSignals();
|
wireDaemonReconnectSignals();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class IpcInterface
|
|||||||
|
|
||||||
// Route functions
|
// Route functions
|
||||||
SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
|
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 clearSavedRoutes() );
|
||||||
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
|
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
|
||||||
SLOT( bool addExclusionRoute(const QString &ifname, const QString &addr) );
|
SLOT( bool addExclusionRoute(const QString &ifname, const QString &addr) );
|
||||||
|
|||||||
@@ -97,6 +97,15 @@ int IpcServer::routeAddList(const QString &gw, const QStringList &ips)
|
|||||||
return Router::routeAddList(gw, 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()
|
bool IpcServer::clearSavedRoutes()
|
||||||
{
|
{
|
||||||
#ifdef MZ_DEBUG
|
#ifdef MZ_DEBUG
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
virtual int createPrivilegedProcess() override;
|
virtual int createPrivilegedProcess() override;
|
||||||
|
|
||||||
virtual int routeAddList(const QString &gw, const QStringList &ips) 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 clearSavedRoutes() override;
|
||||||
virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override;
|
virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override;
|
||||||
virtual bool addExclusionRoute(const QString &ifname, const QString &addr) override;
|
virtual bool addExclusionRoute(const QString &ifname, const QString &addr) override;
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ int Router::routeAddList(const QString &gw, const QStringList &ips)
|
|||||||
#endif
|
#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()
|
bool Router::clearSavedRoutes()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class Router : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static int routeAddList(const QString &gw, const QStringList &ips);
|
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 bool clearSavedRoutes();
|
||||||
static int routeDeleteList(const QString &gw, const QStringList &ips);
|
static int routeDeleteList(const QString &gw, const QStringList &ips);
|
||||||
static bool flushDns();
|
static bool flushDns();
|
||||||
|
|||||||
@@ -87,6 +87,34 @@ int RouterLinux::routeAddList(const QString &gw, const QStringList &ips)
|
|||||||
return cnt;
|
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()
|
bool RouterLinux::clearSavedRoutes()
|
||||||
{
|
{
|
||||||
int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
|
|
||||||
bool routeAdd(const QString &ip, const QString &gw, const int &sock);
|
bool routeAdd(const QString &ip, const QString &gw, const int &sock);
|
||||||
int routeAddList(const QString &gw, const QStringList &ips);
|
int routeAddList(const QString &gw, const QStringList &ips);
|
||||||
|
int routeAddListVia(const QString &ifname, const QString &gw, const QStringList &ips);
|
||||||
bool clearSavedRoutes();
|
bool clearSavedRoutes();
|
||||||
bool routeDelete(const QString &ip, const QString &gw, const int &sock);
|
bool routeDelete(const QString &ip, const QString &gw, const int &sock);
|
||||||
bool routeDeleteList(const QString &gw, const QStringList &ips);
|
bool routeDeleteList(const QString &gw, const QStringList &ips);
|
||||||
|
|||||||
Reference in New Issue
Block a user