refactor: move routing/KS/DNS lifecycle from Daemon to TrafficGuard

This commit is contained in:
cd-amn
2026-05-19 12:25:22 +00:00
parent cfd435ebe4
commit fbd86181cd
20 changed files with 319 additions and 227 deletions
+151 -7
View File
@@ -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();
}
}
+14 -1
View File
@@ -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
View File
@@ -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);
}
+7 -4
View File
@@ -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;
+9 -6
View File
@@ -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);
}
+4 -2
View File
@@ -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
View File
@@ -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;
+6
View File
@@ -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() );
+32
View File
@@ -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
+7
View File
@@ -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;
+10 -4
View File
@@ -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