mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-19 02:00:45 +07:00
refactor: move routing/KS/DNS lifecycle from Daemon to TrafficGuard
This commit is contained in:
@@ -13,6 +13,8 @@
|
||||
#endif
|
||||
|
||||
#include "core/utils/networkUtilities.h"
|
||||
#include "core/tunnel.h"
|
||||
#include "mozilla/localsocketcontroller.h"
|
||||
|
||||
VpnTrafficGuard::VpnTrafficGuard(SecureAppSettingsRepository* appSettings, QObject *parent)
|
||||
: QObject(parent), m_appSettingsRepository(appSettings)
|
||||
@@ -230,29 +232,30 @@ void VpnTrafficGuard::applyFirewall(const QString &gateway, const QString &local
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::teardown()
|
||||
void VpnTrafficGuard::flushAll()
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
iface->restoreTunnelResolvers();
|
||||
QRemoteObjectPendingReply<bool> reply = iface->disableKillSwitch();
|
||||
m_allowedEndpoints.clear();
|
||||
//TODO: why it takes so long?
|
||||
if (!reply.waitForFinished(5000) || !reply.returnValue()) {
|
||||
qWarning() << "VpnTrafficGuard::teardown: Failed to disable killswitch";
|
||||
qWarning() << "VpnTrafficGuard::flushAll: Failed to disable killswitch";
|
||||
} else {
|
||||
qDebug() << "VpnTrafficGuard::teardown: Successfully disabled killswitch";
|
||||
qDebug() << "VpnTrafficGuard::flushAll: Successfully disabled killswitch";
|
||||
}
|
||||
auto flushDns = iface->flushDns();
|
||||
if (flushDns.waitForFinished() && flushDns.returnValue())
|
||||
qDebug() << "VpnTrafficGuard::teardown: Successfully flushed DNS";
|
||||
qDebug() << "VpnTrafficGuard::flushAll: Successfully flushed DNS";
|
||||
else
|
||||
qWarning() << "VpnTrafficGuard::teardown: Failed to flush DNS";
|
||||
qWarning() << "VpnTrafficGuard::flushAll: Failed to flush DNS";
|
||||
|
||||
auto clearSavedRoutes = iface->clearSavedRoutes();
|
||||
if (clearSavedRoutes.waitForFinished() && clearSavedRoutes.returnValue())
|
||||
qDebug() << "VpnTrafficGuard::teardown: Successfully cleared saved routes";
|
||||
qDebug() << "VpnTrafficGuard::flushAll: Successfully cleared saved routes";
|
||||
else
|
||||
qWarning() << "VpnTrafficGuard::teardown: Failed to clear saved routes";
|
||||
qWarning() << "VpnTrafficGuard::flushAll: Failed to clear saved routes";
|
||||
if (m_ipv6RoutingStopped) {
|
||||
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
|
||||
if (!StartRoutingIpv6.waitForFinished() || !StartRoutingIpv6.returnValue()) {
|
||||
@@ -264,3 +267,144 @@ void VpnTrafficGuard::teardown()
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
QStringList allowedIpPrefixesFor(const QJsonObject& activateJson)
|
||||
{
|
||||
QStringList prefixes;
|
||||
const QJsonArray ranges = activateJson.value("allowedIPAddressRanges").toArray();
|
||||
for (const QJsonValue& v : ranges) {
|
||||
const QJsonObject r = v.toObject();
|
||||
const QString addr = r.value("address").toString();
|
||||
if (addr.isEmpty()) continue;
|
||||
prefixes.append(QStringLiteral("%1/%2").arg(addr).arg(r.value("range").toInt()));
|
||||
}
|
||||
return prefixes;
|
||||
}
|
||||
|
||||
QStringList excludedAddressesFor(const QJsonObject& activateJson)
|
||||
{
|
||||
QStringList addrs;
|
||||
const QJsonArray excluded = activateJson.value("excludedAddresses").toArray();
|
||||
for (const QJsonValue& v : excluded) {
|
||||
const QString s = v.toString();
|
||||
if (!s.isEmpty()) addrs.append(s);
|
||||
}
|
||||
return addrs;
|
||||
}
|
||||
|
||||
QStringList resolversFor(const QJsonObject& activateJson)
|
||||
{
|
||||
QStringList dns;
|
||||
const QString primary = activateJson.value("primaryDnsServer").toString();
|
||||
if (!primary.isEmpty()) dns.append(primary);
|
||||
const QString secondary = activateJson.value("secondaryDnsServer").toString();
|
||||
if (!secondary.isEmpty()) dns.append(secondary);
|
||||
return dns;
|
||||
}
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::reserve(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
allowEndpoint(tunnel->remoteAddress());
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::release(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
revokeEndpoint(tunnel->remoteAddress());
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::applyPolicy(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
const QJsonObject activate = LocalSocketController::buildActivateJson(tunnel->config(), tunnel->ifname());
|
||||
const QStringList prefixes = allowedIpPrefixesFor(activate);
|
||||
const QStringList excluded = excludedAddressesFor(activate);
|
||||
const QStringList dns = resolversFor(activate);
|
||||
const QString ifname = tunnel->ifname();
|
||||
const QString peer = tunnel->remoteAddress();
|
||||
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
if (!peer.isEmpty()) iface->addExclusionRoute(peer);
|
||||
for (const QString& addr : excluded) {
|
||||
iface->addExclusionRoute(addr);
|
||||
}
|
||||
for (const QString& prefix : prefixes) {
|
||||
iface->addAllowedIp(ifname, prefix);
|
||||
}
|
||||
iface->setTunnelResolvers(ifname, dns);
|
||||
iface->flushDns();
|
||||
});
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::revokePolicy(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
const QJsonObject activate = LocalSocketController::buildActivateJson(tunnel->config(), tunnel->ifname());
|
||||
const QStringList prefixes = allowedIpPrefixesFor(activate);
|
||||
const QStringList excluded = excludedAddressesFor(activate);
|
||||
const QString ifname = tunnel->ifname();
|
||||
const QString peer = tunnel->remoteAddress();
|
||||
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
for (const QString& prefix : prefixes) {
|
||||
iface->delAllowedIp(ifname, prefix);
|
||||
}
|
||||
for (const QString& addr : excluded) {
|
||||
iface->delExclusionRoute(addr);
|
||||
}
|
||||
if (!peer.isEmpty()) iface->delExclusionRoute(peer);
|
||||
});
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::bringUp(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
reserve(tunnel);
|
||||
tunnel->prepare();
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::commit(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
applyPolicy(tunnel);
|
||||
tunnel->commit();
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::tearDown(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
revokePolicy(tunnel);
|
||||
release(tunnel);
|
||||
tunnel->deactivate();
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::swap(Tunnel* from, Tunnel* to)
|
||||
{
|
||||
if (!to) return;
|
||||
applyPolicy(to);
|
||||
to->commit();
|
||||
if (from) {
|
||||
revokePolicy(from);
|
||||
release(from);
|
||||
from->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "core/repositories/secureAppSettingsRepository.h"
|
||||
#include "protocols/vpnProtocol.h"
|
||||
|
||||
class Tunnel;
|
||||
|
||||
class VpnTrafficGuard : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -17,10 +19,21 @@ public:
|
||||
const QSharedPointer<VpnProtocol> &protocol,
|
||||
const QString &remoteAddress);
|
||||
|
||||
void teardown();
|
||||
void flushAll();
|
||||
bool allowEndpoint(const QString &remoteAddress);
|
||||
void revokeEndpoint(const QString &remoteAddress);
|
||||
void applyFirewall(const QString &vpnGateway, const QString &vpnLocalAddress);
|
||||
|
||||
void reserve(Tunnel* tunnel);
|
||||
void release(Tunnel* tunnel);
|
||||
void applyPolicy(Tunnel* tunnel);
|
||||
void revokePolicy(Tunnel* tunnel);
|
||||
|
||||
void bringUp(Tunnel* tunnel);
|
||||
void commit(Tunnel* tunnel);
|
||||
void tearDown(Tunnel* tunnel);
|
||||
void swap(Tunnel* from, Tunnel* to);
|
||||
|
||||
private:
|
||||
void addSplitTunnelRoutes(const QString &gateway, amnezia::RouteMode mode);
|
||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||
|
||||
+48
-102
@@ -101,13 +101,6 @@ bool Daemon::activate(const QString& ifname, const InterfaceConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.m_serverIpv4AddrIn.isEmpty()) {
|
||||
addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
}
|
||||
if (!config.m_serverIpv6AddrIn.isEmpty()) {
|
||||
addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
// Add the peer to this interface.
|
||||
if (!wg->updatePeer(config)) {
|
||||
logger.error() << "Peer creation failed.";
|
||||
@@ -138,90 +131,32 @@ bool Daemon::setPrimary(const QString& ifname, const InterfaceConfig& config) {
|
||||
m_primaryIfname = priorPrimary;
|
||||
});
|
||||
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
addExclusionRoute(IPAddress(i));
|
||||
}
|
||||
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wg->updateRoutePrefix(ip)) {
|
||||
logger.warning() << "setPrimary: route setup failed for" << ip.toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
logger.warning() << "setPrimary: DNS resolver update failed";
|
||||
}
|
||||
|
||||
if (!run(Up, config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connections[ifname].m_config = config;
|
||||
|
||||
// Demote the prior primary AFTER the new primary is fully installed.
|
||||
// Delete-after-install order preserves coverage during the make-before-break overlap.
|
||||
if (!priorPrimary.isEmpty() && priorPrimary != ifname) {
|
||||
demotePrimary(priorPrimary);
|
||||
}
|
||||
|
||||
failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Daemon::demotePrimary(const QString& ifname) {
|
||||
WireguardUtils* wg = wgutilsFor(ifname);
|
||||
if (!wg) {
|
||||
return;
|
||||
}
|
||||
const ConnectionState cs = m_connections.value(ifname);
|
||||
const InterfaceConfig& config = cs.m_config;
|
||||
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wg->deleteRoutePrefix(ip);
|
||||
}
|
||||
for (const QString& addr : config.m_excludedAddresses) {
|
||||
if (addr.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
IPAddress ip(addr);
|
||||
if (m_excludedAddrSet.contains(ip)) {
|
||||
delExclusionRoute(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Daemon::deactivateTunnel(const QString& ifname) {
|
||||
WireguardUtils* wg = m_tunnels.value(ifname);
|
||||
const ConnectionState cs = m_connections.value(ifname);
|
||||
const InterfaceConfig& config = cs.m_config;
|
||||
const bool wasPrimary = (ifname == m_primaryIfname);
|
||||
const bool isLastTunnel = wg && m_tunnels.size() == 1;
|
||||
|
||||
if (wg) {
|
||||
logger.debug() << "deactivateTunnel" << wg->interfaceName();
|
||||
if (wasPrimary) {
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wg->deleteRoutePrefix(ip);
|
||||
if (isLastTunnel) {
|
||||
for (const IPAddress& prefix : m_excludedAddrSet.keys()) {
|
||||
wg->deleteExclusionRoute(prefix);
|
||||
}
|
||||
m_excludedAddrSet.clear();
|
||||
}
|
||||
wg->deletePeer(config);
|
||||
|
||||
auto removeExclusion = [&](const QString& addr) {
|
||||
if (addr.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
IPAddress ip(addr);
|
||||
if (m_excludedAddrSet.contains(ip)) {
|
||||
delExclusionRoute(ip);
|
||||
}
|
||||
};
|
||||
removeExclusion(config.m_serverIpv4AddrIn);
|
||||
removeExclusion(config.m_serverIpv6AddrIn);
|
||||
if (wasPrimary) {
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
removeExclusion(i);
|
||||
}
|
||||
}
|
||||
|
||||
wg->deleteInterface();
|
||||
m_tunnels.remove(ifname);
|
||||
delete wg;
|
||||
@@ -234,30 +169,6 @@ bool Daemon::deactivateTunnel(const QString& ifname) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
||||
QList<QHostAddress> resolvers;
|
||||
resolvers.append(QHostAddress(config.m_primaryDnsServer));
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
resolvers.append(QHostAddress(config.m_secondaryDnsServer));
|
||||
}
|
||||
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
// thus, not add any other :)
|
||||
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) {
|
||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||
}
|
||||
|
||||
const QString ifname = wgutilsFor(config.m_ifname)->interfaceName();
|
||||
if (!dnsutils()->updateResolvers(ifname, resolvers)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseStringList(const QJsonObject& obj, const QString& name,
|
||||
QStringList& list) {
|
||||
@@ -279,20 +190,25 @@ bool Daemon::parseStringList(const QJsonObject& obj, const QString& name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::addExclusionRoute(const IPAddress& prefix) {
|
||||
bool Daemon::addExclusionRoute(const QString &addr) {
|
||||
IPAddress prefix(addr);
|
||||
if (m_excludedAddrSet.contains(prefix)) {
|
||||
m_excludedAddrSet[prefix]++;
|
||||
return true;
|
||||
}
|
||||
if (!primaryWgutils()->addExclusionRoute(prefix)) {
|
||||
WireguardUtils* wg = primaryWgutils();
|
||||
if (!wg || !wg->addExclusionRoute(prefix)) {
|
||||
return false;
|
||||
}
|
||||
m_excludedAddrSet[prefix] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::delExclusionRoute(const IPAddress& prefix) {
|
||||
Q_ASSERT(m_excludedAddrSet.contains(prefix));
|
||||
bool Daemon::delExclusionRoute(const QString &addr) {
|
||||
IPAddress prefix(addr);
|
||||
if (!m_excludedAddrSet.contains(prefix)) {
|
||||
return false;
|
||||
}
|
||||
if (m_excludedAddrSet[prefix] > 1) {
|
||||
m_excludedAddrSet[prefix]--;
|
||||
return true;
|
||||
@@ -302,6 +218,32 @@ bool Daemon::delExclusionRoute(const IPAddress& prefix) {
|
||||
return wg && wg->deleteExclusionRoute(prefix);
|
||||
}
|
||||
|
||||
bool Daemon::addAllowedIp(const QString &ifname, const QString &prefix) {
|
||||
WireguardUtils* wg = wgutilsFor(ifname);
|
||||
return wg && wg->updateRoutePrefix(IPAddress(prefix));
|
||||
}
|
||||
|
||||
bool Daemon::delAllowedIp(const QString &ifname, const QString &prefix) {
|
||||
WireguardUtils* wg = wgutilsFor(ifname);
|
||||
return wg && wg->deleteRoutePrefix(IPAddress(prefix));
|
||||
}
|
||||
|
||||
bool Daemon::setTunnelResolvers(const QString &ifname, const QStringList &resolvers) {
|
||||
WireguardUtils* wg = wgutilsFor(ifname);
|
||||
if (!wg || !dnsutils()) {
|
||||
return false;
|
||||
}
|
||||
QList<QHostAddress> hostAddrs;
|
||||
for (const QString& r : resolvers) {
|
||||
hostAddrs.append(QHostAddress(r));
|
||||
}
|
||||
return dnsutils()->updateResolvers(wg->interfaceName(), hostAddrs);
|
||||
}
|
||||
|
||||
bool Daemon::restoreTunnelResolvers() {
|
||||
return dnsutils() && dnsutils()->restoreResolvers();
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
#define GETVALUE(name, where, jsontype) \
|
||||
@@ -529,16 +471,20 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
if (!dnsutils()->restoreResolvers()) {
|
||||
logger.warning() << "Failed to restore DNS resolvers.";
|
||||
}
|
||||
|
||||
const QStringList ifnames = m_tunnels.keys();
|
||||
for (const QString& ifname : ifnames) {
|
||||
if (ifname != primary) {
|
||||
deactivateTunnel(ifname);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* wg = primaryWgutils()) {
|
||||
for (const IPAddress& prefix : m_excludedAddrSet.keys()) {
|
||||
wg->deleteExclusionRoute(prefix);
|
||||
}
|
||||
}
|
||||
m_excludedAddrSet.clear();
|
||||
|
||||
if (m_tunnels.contains(primary)) {
|
||||
deactivateTunnel(primary);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,13 @@ class Daemon : public QObject {
|
||||
virtual bool deactivate(bool emitSignals = true);
|
||||
virtual QJsonObject getStatus();
|
||||
|
||||
bool addExclusionRoute(const QString &addr);
|
||||
bool delExclusionRoute(const QString &addr);
|
||||
bool addAllowedIp(const QString &ifname, const QString &prefix);
|
||||
bool delAllowedIp(const QString &ifname, const QString &prefix);
|
||||
bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers);
|
||||
bool restoreTunnelResolvers();
|
||||
|
||||
const QString& primaryIfname() const { return m_primaryIfname; }
|
||||
WireguardUtils* wgutilsFor(const QString& ifname) const { return m_tunnels.value(ifname); }
|
||||
|
||||
@@ -56,10 +63,6 @@ class Daemon : public QObject {
|
||||
void backendFailure(DaemonError reason = DaemonError::ERROR_FATAL);
|
||||
|
||||
private:
|
||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||
bool addExclusionRoute(const IPAddress& address);
|
||||
bool delExclusionRoute(const IPAddress& address);
|
||||
void demotePrimary(const QString& ifname);
|
||||
void checkActivations();
|
||||
WireguardUtils* primaryWgutils() const { return m_tunnels.value(m_primaryIfname); }
|
||||
QTimer m_activationTimer;
|
||||
|
||||
@@ -122,7 +122,8 @@ void LocalSocketController::daemonConnected() {
|
||||
checkStatus();
|
||||
}
|
||||
|
||||
QJsonObject LocalSocketController::buildActivateJson(const QJsonObject& rawConfig) {
|
||||
QJsonObject LocalSocketController::buildActivateJson(const QJsonObject& rawConfig,
|
||||
const QString& ifname) {
|
||||
QString protocolName = rawConfig.value("protocol").toString();
|
||||
|
||||
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
|
||||
@@ -138,7 +139,6 @@ QJsonObject LocalSocketController::buildActivateJson(const QJsonObject& rawConfi
|
||||
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
|
||||
json.insert("privateKey", wgConfig.value(amnezia::configKey::clientPrivKey));
|
||||
json.insert("deviceIpv4Address", wgConfig.value(amnezia::configKey::clientIp));
|
||||
m_deviceIpv4 = wgConfig.value(amnezia::configKey::clientIp).toString();
|
||||
|
||||
// set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable.
|
||||
// this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4.
|
||||
@@ -230,7 +230,6 @@ QJsonObject LocalSocketController::buildActivateJson(const QJsonObject& rawConfi
|
||||
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
||||
|
||||
QJsonArray jsExcludedAddresses;
|
||||
jsExcludedAddresses.append(wgConfig.value(amnezia::configKey::hostName));
|
||||
if (splitTunnelType == 2) {
|
||||
for (auto v : splitTunnelSites) {
|
||||
QString ipRange = v.toString();
|
||||
@@ -292,18 +291,22 @@ QJsonObject LocalSocketController::buildActivateJson(const QJsonObject& rawConfi
|
||||
json.insert(amnezia::configKey::specialJunk5, wgConfig.value(amnezia::configKey::specialJunk5));
|
||||
}
|
||||
|
||||
json.insert("ifname", m_ifname);
|
||||
json.insert("ifname", ifname);
|
||||
return json;
|
||||
}
|
||||
|
||||
void LocalSocketController::activate(const QJsonObject& rawConfig) {
|
||||
QJsonObject json = buildActivateJson(rawConfig);
|
||||
const QString protocolName = rawConfig.value("protocol").toString();
|
||||
const QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||
m_deviceIpv4 = wgConfig.value(amnezia::configKey::clientIp).toString();
|
||||
|
||||
QJsonObject json = buildActivateJson(rawConfig, m_ifname);
|
||||
json.insert("type", "activate");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::setPrimary(const QJsonObject& rawConfig) {
|
||||
QJsonObject json = buildActivateJson(rawConfig);
|
||||
QJsonObject json = buildActivateJson(rawConfig, m_ifname);
|
||||
json.insert("type", "setPrimary");
|
||||
write(json);
|
||||
}
|
||||
|
||||
@@ -38,12 +38,14 @@ class LocalSocketController final : public ControllerImpl {
|
||||
|
||||
bool multihopSupported() override { return true; }
|
||||
|
||||
public:
|
||||
static QJsonObject buildActivateJson(const QJsonObject& rawConfig,
|
||||
const QString& ifname);
|
||||
|
||||
private:
|
||||
void initializeInternal();
|
||||
void disconnectInternal();
|
||||
|
||||
QJsonObject buildActivateJson(const QJsonObject& rawConfig);
|
||||
|
||||
void daemonConnected();
|
||||
void errorOccurred(QLocalSocket::LocalSocketError socketError);
|
||||
void readData();
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "killswitch.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("LinuxDaemon");
|
||||
@@ -51,8 +50,3 @@ LinuxDaemon* LinuxDaemon::instance() {
|
||||
return s_daemon;
|
||||
}
|
||||
|
||||
bool LinuxDaemon::deactivate(bool emitSignals) {
|
||||
bool result = Daemon::deactivate(emitSignals);
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ class LinuxDaemon final : public Daemon {
|
||||
|
||||
static LinuxDaemon* instance();
|
||||
|
||||
bool deactivate(bool emitSignals = true) override;
|
||||
|
||||
protected:
|
||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||
bool supportIPUtils() const override { return true; }
|
||||
|
||||
@@ -208,15 +208,6 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
out << "allowed_ip=" << ip.toString() << "\n";
|
||||
}
|
||||
|
||||
// Exclude the server address, except for multihop exit servers.
|
||||
if ((config.m_hopType != InterfaceConfig::MultiHopExit) &&
|
||||
(m_rtmonitor != nullptr)) {
|
||||
if (!config.m_serverIpv4AddrIn.isEmpty())
|
||||
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
if (!config.m_serverIpv6AddrIn.isEmpty())
|
||||
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
logger.error() << "Peer configuration failed:" << strerror(err);
|
||||
@@ -228,15 +219,6 @@ bool WireguardUtilsLinux::deletePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
|
||||
// Clear exclustion routes for this peer.
|
||||
if ((config.m_hopType != InterfaceConfig::MultiHopExit) &&
|
||||
(m_rtmonitor != nullptr)) {
|
||||
if (!config.m_serverIpv4AddrIn.isEmpty())
|
||||
m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
if (!config.m_serverIpv6AddrIn.isEmpty())
|
||||
m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
QString message;
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
|
||||
@@ -185,6 +185,9 @@ bool DnsUtilsMacos::restoreResolvers() {
|
||||
}
|
||||
|
||||
void DnsUtilsMacos::backupService(const QString& uuid) {
|
||||
if (m_prevServices.contains(uuid)) {
|
||||
return;
|
||||
}
|
||||
DnsBackup backup;
|
||||
CFStringRef path = CFStringCreateWithFormat(
|
||||
kCFAllocatorSystemDefault, nullptr,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <QTextStream>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "killswitch.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
@@ -51,8 +50,3 @@ MacOSDaemon* MacOSDaemon::instance() {
|
||||
return s_daemon;
|
||||
}
|
||||
|
||||
bool MacOSDaemon::deactivate(bool emitSignals) {
|
||||
bool result = Daemon::deactivate(emitSignals);
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ class MacOSDaemon final : public Daemon {
|
||||
|
||||
static MacOSDaemon* instance();
|
||||
|
||||
bool deactivate(bool emitSignals = true) override;
|
||||
|
||||
protected:
|
||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||
bool supportIPUtils() const override { return true; }
|
||||
|
||||
@@ -51,7 +51,6 @@ MacosRouteMonitor::MacosRouteMonitor(const QString& ifname, QObject* parent)
|
||||
|
||||
MacosRouteMonitor::~MacosRouteMonitor() {
|
||||
MZ_COUNT_DTOR(MacosRouteMonitor);
|
||||
flushExclusionRoutes();
|
||||
if (m_rtsock >= 0) {
|
||||
close(m_rtsock);
|
||||
}
|
||||
|
||||
@@ -204,13 +204,6 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||
out << "allowed_ip=" << ip.toString() << "\n";
|
||||
}
|
||||
|
||||
// Exclude the server address, except for multihop exit servers.
|
||||
if ((config.m_hopType != InterfaceConfig::MultiHopExit) &&
|
||||
(m_rtmonitor != nullptr)) {
|
||||
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
logger.error() << "Peer configuration failed:" << strerror(err);
|
||||
@@ -222,13 +215,6 @@ bool WireguardUtilsMacos::deletePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
|
||||
// Clear exclustion routes for this peer.
|
||||
if ((config.m_hopType != InterfaceConfig::MultiHopExit) &&
|
||||
(m_rtmonitor != nullptr)) {
|
||||
m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
QString message;
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
|
||||
@@ -185,12 +185,6 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||
out << "allowed_ip=" << ip.toString() << "\n";
|
||||
}
|
||||
|
||||
// Exclude the server address, except for multihop exit servers.
|
||||
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
|
||||
m_routeMonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
m_routeMonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
QString reply = m_tunnel.uapiCommand(message);
|
||||
logger.debug() << "DATA:" << reply;
|
||||
return true;
|
||||
@@ -200,12 +194,6 @@ bool WireguardUtilsWindows::deletePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
|
||||
// Clear exclustion routes for this peer.
|
||||
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
|
||||
m_routeMonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
m_routeMonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
// Disable the windows firewall for this peer.
|
||||
m_firewall->disablePeerTraffic(config.m_serverPublicKey);
|
||||
|
||||
|
||||
+28
-40
@@ -146,9 +146,6 @@ void VpnConnection::wireTunnelSignals(Tunnel* tunnel, bool isActive)
|
||||
|
||||
if (isActive) {
|
||||
connect(tunnel, &Tunnel::bytesChanged, this, &VpnConnection::onBytesChanged);
|
||||
// Staging tunnel deliberately skips this wire: applying KS while the old
|
||||
// primary is still serving would clobber its allow-rules. onTunnelActivated
|
||||
// invokes applyFirewall manually after the make-before-break swap.
|
||||
connect(tunnel, &Tunnel::addressesUpdated,
|
||||
m_trafficGuard.data(), &VpnTrafficGuard::applyFirewall);
|
||||
}
|
||||
@@ -197,14 +194,14 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_active) {
|
||||
const QString oldIfname = m_active->ifname();
|
||||
m_active->deactivate();
|
||||
m_trafficGuard->tearDown(m_active);
|
||||
delete m_active;
|
||||
m_active = nullptr;
|
||||
releaseIfname(oldIfname);
|
||||
}
|
||||
if (m_vpnProtocol) {
|
||||
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||
m_trafficGuard->teardown();
|
||||
m_trafficGuard->flushAll();
|
||||
m_vpnProtocol->stop();
|
||||
m_vpnProtocol.reset();
|
||||
}
|
||||
@@ -223,7 +220,7 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
wireTunnelSignals(m_active, /*isActive=*/true);
|
||||
wireDaemonReconnectSignals();
|
||||
m_trafficGuard->setConfig(config);
|
||||
m_active->prepare();
|
||||
m_trafficGuard->bringUp(m_active);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -476,8 +473,7 @@ void VpnConnection::disconnectFromVpn()
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_staging) {
|
||||
m_trafficGuard->revokeEndpoint(m_staging->remoteAddress());
|
||||
m_staging->deactivate();
|
||||
m_trafficGuard->tearDown(m_staging);
|
||||
releaseIfname(m_staging->ifname());
|
||||
delete m_staging;
|
||||
m_staging = nullptr;
|
||||
@@ -485,9 +481,8 @@ void VpnConnection::disconnectFromVpn()
|
||||
|
||||
if (m_active) {
|
||||
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
||||
m_trafficGuard->teardown();
|
||||
m_trafficGuard->revokeEndpoint(m_remoteAddress);
|
||||
m_active->deactivate();
|
||||
m_trafficGuard->tearDown(m_active);
|
||||
m_trafficGuard->flushAll();
|
||||
releaseIfname(m_active->ifname());
|
||||
delete m_active;
|
||||
m_active = nullptr;
|
||||
@@ -515,7 +510,7 @@ void VpnConnection::disconnectFromVpn()
|
||||
});
|
||||
#endif
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
m_trafficGuard->teardown();
|
||||
m_trafficGuard->flushAll();
|
||||
#endif
|
||||
m_vpnProtocol->stop();
|
||||
|
||||
@@ -549,31 +544,19 @@ void VpnConnection::startTunnelSwitch(DockerContainer container,
|
||||
wireTunnelSignals(m_staging, /*isActive=*/false);
|
||||
|
||||
setConnectionState(Vpn::ConnectionState::Switching);
|
||||
m_staging->prepare();
|
||||
m_trafficGuard->bringUp(m_staging);
|
||||
}
|
||||
|
||||
void VpnConnection::onTunnelPrepared()
|
||||
{
|
||||
Tunnel* tunnel = qobject_cast<Tunnel*>(sender());
|
||||
if (!tunnel) return;
|
||||
tunnel->commit();
|
||||
}
|
||||
|
||||
void VpnConnection::onTunnelActivated()
|
||||
{
|
||||
Tunnel* tunnel = qobject_cast<Tunnel*>(sender());
|
||||
if (!tunnel) return;
|
||||
|
||||
if (tunnel == m_staging) {
|
||||
// Make-before-break gate passed: new tunnel is primary, old still allowed by KS.
|
||||
if (m_active) {
|
||||
const QString oldRemote = m_active->remoteAddress();
|
||||
const QString oldIfname = m_active->ifname();
|
||||
m_active->deactivate();
|
||||
delete m_active;
|
||||
releaseIfname(oldIfname);
|
||||
m_trafficGuard->revokeEndpoint(oldRemote);
|
||||
}
|
||||
if (tunnel == m_staging && m_active) {
|
||||
const QString oldIfname = m_active->ifname();
|
||||
m_trafficGuard->swap(m_active, m_staging);
|
||||
delete m_active;
|
||||
releaseIfname(oldIfname);
|
||||
|
||||
m_active = m_staging;
|
||||
m_staging = nullptr;
|
||||
@@ -583,17 +566,23 @@ void VpnConnection::onTunnelActivated()
|
||||
m_vpnConfiguration = m_active->config();
|
||||
m_remoteAddress = m_active->remoteAddress();
|
||||
m_trafficGuard->setConfig(m_vpnConfiguration);
|
||||
return;
|
||||
}
|
||||
|
||||
m_trafficGuard->commit(tunnel);
|
||||
}
|
||||
|
||||
void VpnConnection::onTunnelActivated()
|
||||
{
|
||||
Tunnel* tunnel = qobject_cast<Tunnel*>(sender());
|
||||
if (!tunnel) return;
|
||||
|
||||
if (tunnel == m_active) {
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
if (auto proto = m_active->protocol()) {
|
||||
m_trafficGuard->applyFirewall(proto->vpnGateway(),
|
||||
proto->vpnLocalAddress());
|
||||
}
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tunnel == m_active) {
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,7 +592,7 @@ void VpnConnection::onTunnelFailed(amnezia::ErrorCode error)
|
||||
if (!tunnel) return;
|
||||
|
||||
if (tunnel == m_staging) {
|
||||
m_trafficGuard->revokeEndpoint(m_staging->remoteAddress());
|
||||
m_trafficGuard->release(m_staging);
|
||||
m_staging->deactivate();
|
||||
releaseIfname(m_staging->ifname());
|
||||
m_staging->deleteLater();
|
||||
@@ -614,9 +603,8 @@ void VpnConnection::onTunnelFailed(amnezia::ErrorCode error)
|
||||
}
|
||||
|
||||
if (tunnel == m_active) {
|
||||
m_trafficGuard->teardown();
|
||||
m_trafficGuard->revokeEndpoint(m_remoteAddress);
|
||||
m_active->deactivate();
|
||||
m_trafficGuard->tearDown(m_active);
|
||||
m_trafficGuard->flushAll();
|
||||
releaseIfname(m_active->ifname());
|
||||
m_active->deleteLater();
|
||||
m_active = nullptr;
|
||||
|
||||
@@ -12,6 +12,12 @@ class IpcInterface
|
||||
SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
|
||||
SLOT( bool clearSavedRoutes() );
|
||||
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
|
||||
SLOT( bool addExclusionRoute(const QString &addr) );
|
||||
SLOT( bool delExclusionRoute(const QString &addr) );
|
||||
SLOT( bool addAllowedIp(const QString &ifname, const QString &prefix) );
|
||||
SLOT( bool delAllowedIp(const QString &ifname, const QString &prefix) );
|
||||
SLOT( bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) );
|
||||
SLOT( bool restoreTunnelResolvers() );
|
||||
SLOT( bool flushDns() );
|
||||
SLOT( void resetIpStack() );
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "killswitch.h"
|
||||
#include "xray.h"
|
||||
|
||||
#include "../client/daemon/daemon.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "tapcontroller_win.h"
|
||||
#endif
|
||||
@@ -91,6 +93,36 @@ bool IpcServer::routeDeleteList(const QString &gw, const QStringList &ips)
|
||||
return Router::routeDeleteList(gw, ips);
|
||||
}
|
||||
|
||||
bool IpcServer::addExclusionRoute(const QString &addr)
|
||||
{
|
||||
return Daemon::instance() && Daemon::instance()->addExclusionRoute(addr);
|
||||
}
|
||||
|
||||
bool IpcServer::delExclusionRoute(const QString &addr)
|
||||
{
|
||||
return Daemon::instance() && Daemon::instance()->delExclusionRoute(addr);
|
||||
}
|
||||
|
||||
bool IpcServer::addAllowedIp(const QString &ifname, const QString &prefix)
|
||||
{
|
||||
return Daemon::instance() && Daemon::instance()->addAllowedIp(ifname, prefix);
|
||||
}
|
||||
|
||||
bool IpcServer::delAllowedIp(const QString &ifname, const QString &prefix)
|
||||
{
|
||||
return Daemon::instance() && Daemon::instance()->delAllowedIp(ifname, prefix);
|
||||
}
|
||||
|
||||
bool IpcServer::setTunnelResolvers(const QString &ifname, const QStringList &resolvers)
|
||||
{
|
||||
return Daemon::instance() && Daemon::instance()->setTunnelResolvers(ifname, resolvers);
|
||||
}
|
||||
|
||||
bool IpcServer::restoreTunnelResolvers()
|
||||
{
|
||||
return Daemon::instance() && Daemon::instance()->restoreTunnelResolvers();
|
||||
}
|
||||
|
||||
bool IpcServer::flushDns()
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
|
||||
@@ -17,11 +17,18 @@ class IpcServer : public IpcInterfaceSource
|
||||
{
|
||||
public:
|
||||
explicit IpcServer(QObject *parent = nullptr);
|
||||
|
||||
virtual int createPrivilegedProcess() override;
|
||||
|
||||
virtual int routeAddList(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 &addr) override;
|
||||
virtual bool delExclusionRoute(const QString &addr) override;
|
||||
virtual bool addAllowedIp(const QString &ifname, const QString &prefix) override;
|
||||
virtual bool delAllowedIp(const QString &ifname, const QString &prefix) override;
|
||||
virtual bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) override;
|
||||
virtual bool restoreTunnelResolvers() override;
|
||||
virtual bool flushDns() override;
|
||||
virtual void resetIpStack() override;
|
||||
virtual bool checkAndInstallDriver() override;
|
||||
|
||||
@@ -56,14 +56,20 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Signal handling for a proper shutdown.
|
||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit,
|
||||
[]() { LinuxDaemon::instance()->deactivate(); });
|
||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this]() {
|
||||
LinuxDaemon::instance()->deactivate();
|
||||
m_ipcServer.restoreTunnelResolvers();
|
||||
m_ipcServer.disableKillSwitch();
|
||||
});
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// Signal handling for a proper shutdown.
|
||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit,
|
||||
[]() { MacOSDaemon::instance()->deactivate(); });
|
||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this]() {
|
||||
MacOSDaemon::instance()->deactivate();
|
||||
m_ipcServer.restoreTunnelResolvers();
|
||||
m_ipcServer.disableKillSwitch();
|
||||
});
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
Reference in New Issue
Block a user