mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
feat: split daemon activation into bare bring-up and setPrimary
This commit is contained in:
+201
-190
@@ -34,8 +34,8 @@ Daemon::Daemon(QObject* parent) : QObject(parent) {
|
||||
Q_ASSERT(s_daemon == nullptr);
|
||||
s_daemon = this;
|
||||
|
||||
m_handshakeTimer.setSingleShot(true);
|
||||
connect(&m_handshakeTimer, &QTimer::timeout, this, &Daemon::checkHandshake);
|
||||
m_activationTimer.setSingleShot(false);
|
||||
connect(&m_activationTimer, &QTimer::timeout, this, &Daemon::checkActivations);
|
||||
}
|
||||
|
||||
Daemon::~Daemon() {
|
||||
@@ -43,6 +43,9 @@ Daemon::~Daemon() {
|
||||
|
||||
logger.debug() << "Daemon released";
|
||||
|
||||
qDeleteAll(m_tunnels);
|
||||
m_tunnels.clear();
|
||||
|
||||
Q_ASSERT(s_daemon == this);
|
||||
s_daemon = nullptr;
|
||||
}
|
||||
@@ -53,69 +56,36 @@ Daemon* Daemon::instance() {
|
||||
return s_daemon;
|
||||
}
|
||||
|
||||
bool Daemon::activate(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
bool Daemon::activate(const QString& ifname, const InterfaceConfig& config) {
|
||||
logger.debug() << "Activating tunnel";
|
||||
|
||||
// There are 3 possible scenarios in which this method is called:
|
||||
//
|
||||
// 1. the VPN is off: the method tries to enable the VPN.
|
||||
// 2. the VPN is on and the platform doesn't support the server-switching:
|
||||
// this method calls deactivate() and then it continues as 1.
|
||||
// 3. the VPN is on and the platform supports the server-switching: this
|
||||
// method calls switchServer().
|
||||
//
|
||||
// At the end, if the activation succeds, the `connected` signal is emitted.
|
||||
// If the activation abort's for any reason `the `activationFailure` signal is
|
||||
// emitted.
|
||||
logger.debug() << "Activating interface";
|
||||
auto emit_failure_guard = qScopeGuard([this] { emit activationFailure(); });
|
||||
|
||||
if (m_connections.contains(config.m_hopType)) {
|
||||
if (supportServerSwitching(config)) {
|
||||
logger.debug() << "Already connected. Server switching supported.";
|
||||
|
||||
if (!switchServer(config)) {
|
||||
WireguardUtils* wg = m_tunnels.value(ifname);
|
||||
if (!wg) {
|
||||
wg = createWgUtils();
|
||||
if (!wg) {
|
||||
logger.error() << "Failed to create wireguard utils.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dnsutils()->restoreResolvers()) {
|
||||
return false;
|
||||
m_tunnels.insert(ifname, wg);
|
||||
}
|
||||
if (m_primaryIfname.isEmpty()) {
|
||||
m_primaryIfname = ifname;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
}
|
||||
ConnectionState& cs = m_connections[ifname];
|
||||
cs.m_config = config;
|
||||
cs.m_date = QDateTime();
|
||||
cs.m_deadline = QDateTime::currentDateTime().addMSecs(ACTIVATION_TIMEOUT_MSEC);
|
||||
|
||||
bool status = run(Switch, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopType] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
emit_failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.warning() << "Already connected. Server switching not supported.";
|
||||
if (!deactivate(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_connections.contains(config.m_hopType));
|
||||
if (activate(config)) {
|
||||
emit_failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
auto failure_guard = qScopeGuard([this, ifname] {
|
||||
deactivateTunnel(ifname);
|
||||
});
|
||||
|
||||
prepareActivation(config);
|
||||
|
||||
// Bring up the wireguard interface if not already done.
|
||||
if (!wgutils()->interfaceExists()) {
|
||||
// Create the interface.
|
||||
if (!wgutils()->addInterface(config)) {
|
||||
if (!wg->interfaceExists()) {
|
||||
if (!wg->addInterface(config)) {
|
||||
logger.error() << "Interface creation failed.";
|
||||
return false;
|
||||
}
|
||||
@@ -131,38 +101,137 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure routing for excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
addExclusionRoute(IPAddress(i));
|
||||
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 (!wgutils()->updatePeer(config)) {
|
||||
if (!wg->updatePeer(config)) {
|
||||
logger.error() << "Peer creation failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
if (!m_activationTimer.isActive()) {
|
||||
m_activationTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
|
||||
// set routing
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||
logger.debug() << "Routing configuration failed for" << ip.toString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool status = run(Up, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopType] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
emit_failure_guard.dismiss();
|
||||
failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Daemon::setPrimary(const QString& ifname, const InterfaceConfig& config) {
|
||||
WireguardUtils* wg = m_tunnels.value(ifname);
|
||||
if (!wg) {
|
||||
logger.error() << "setPrimary: no tunnel for" << ifname;
|
||||
return false;
|
||||
}
|
||||
logger.debug() << "setPrimary" << wg->interfaceName();
|
||||
|
||||
const QString priorPrimary = m_primaryIfname;
|
||||
m_primaryIfname = ifname;
|
||||
|
||||
auto failure_guard = qScopeGuard([this, ifname, priorPrimary] {
|
||||
deactivateTunnel(ifname);
|
||||
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);
|
||||
|
||||
if (wg) {
|
||||
logger.debug() << "deactivateTunnel" << wg->interfaceName();
|
||||
if (wasPrimary) {
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wg->deleteRoutePrefix(ip);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
m_connections.remove(ifname);
|
||||
if (wasPrimary) {
|
||||
m_primaryIfname.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||
@@ -180,7 +249,8 @@ bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||
}
|
||||
|
||||
if (!dnsutils()->updateResolvers(wgutils()->interfaceName(), resolvers)) {
|
||||
const QString ifname = wgutilsFor(config.m_ifname)->interfaceName();
|
||||
if (!dnsutils()->updateResolvers(ifname, resolvers)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -214,7 +284,7 @@ bool Daemon::addExclusionRoute(const IPAddress& prefix) {
|
||||
m_excludedAddrSet[prefix]++;
|
||||
return true;
|
||||
}
|
||||
if (!wgutils()->addExclusionRoute(prefix)) {
|
||||
if (!primaryWgutils()->addExclusionRoute(prefix)) {
|
||||
return false;
|
||||
}
|
||||
m_excludedAddrSet[prefix] = 1;
|
||||
@@ -228,7 +298,8 @@ bool Daemon::delExclusionRoute(const IPAddress& prefix) {
|
||||
return true;
|
||||
}
|
||||
m_excludedAddrSet.remove(prefix);
|
||||
return wgutils()->deleteExclusionRoute(prefix);
|
||||
WireguardUtils* wg = primaryWgutils();
|
||||
return wg && wg->deleteExclusionRoute(prefix);
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -440,18 +511,16 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
if (!obj.value("I5").isNull()) {
|
||||
config.m_specialJunk["I5"] = obj.value("I5").toString();
|
||||
}
|
||||
config.m_ifname = obj.value("ifname").toString(WG_INTERFACE);
|
||||
config.m_ifname = obj.value("ifname").toString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::deactivate(bool emitSignals) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
const QString primary = m_primaryIfname;
|
||||
|
||||
// Deactivate the main interface.
|
||||
if (!m_connections.isEmpty()) {
|
||||
const ConnectionState& state = m_connections.first();
|
||||
if (!run(Down, state.m_config)) {
|
||||
if (m_connections.contains(primary)) {
|
||||
if (!run(Down, m_connections.value(primary).m_config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -460,31 +529,22 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
// Cleanup DNS
|
||||
if (!dnsutils()->restoreResolvers()) {
|
||||
logger.warning() << "Failed to restore DNS resolvers.";
|
||||
}
|
||||
|
||||
// Cleanup peers and routing
|
||||
for (const ConnectionState& state : m_connections) {
|
||||
const InterfaceConfig& config = state.m_config;
|
||||
logger.debug() << "Deleting routes for" << config.m_hopType;
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wgutils()->deleteRoutePrefix(ip);
|
||||
const QStringList ifnames = m_tunnels.keys();
|
||||
for (const QString& ifname : ifnames) {
|
||||
if (ifname != primary) {
|
||||
deactivateTunnel(ifname);
|
||||
}
|
||||
wgutils()->deletePeer(config);
|
||||
}
|
||||
if (m_tunnels.contains(primary)) {
|
||||
deactivateTunnel(primary);
|
||||
}
|
||||
|
||||
// Cleanup routing for excluded addresses.
|
||||
for (auto iterator = m_excludedAddrSet.constBegin();
|
||||
iterator != m_excludedAddrSet.constEnd(); ++iterator) {
|
||||
wgutils()->deleteExclusionRoute(iterator.key());
|
||||
}
|
||||
m_excludedAddrSet.clear();
|
||||
|
||||
m_connections.clear();
|
||||
// Delete the interface
|
||||
return wgutils()->deleteInterface();
|
||||
m_activationTimer.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
QString Daemon::logs() {
|
||||
@@ -493,79 +553,18 @@ QString Daemon::logs() {
|
||||
|
||||
void Daemon::cleanLogs() { }
|
||||
|
||||
bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
|
||||
if (!m_connections.contains(config.m_hopType)) {
|
||||
return false;
|
||||
}
|
||||
const InterfaceConfig& current =
|
||||
m_connections.value(config.m_hopType).m_config;
|
||||
|
||||
return current.m_privateKey == config.m_privateKey &&
|
||||
current.m_deviceIpv4Address == config.m_deviceIpv4Address &&
|
||||
current.m_deviceIpv6Address == config.m_deviceIpv6Address &&
|
||||
current.m_serverIpv4Gateway == config.m_serverIpv4Gateway &&
|
||||
current.m_serverIpv6Gateway == config.m_serverIpv6Gateway;
|
||||
}
|
||||
|
||||
bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
logger.debug() << "Switching server for" << config.m_hopType;
|
||||
|
||||
Q_ASSERT(m_connections.contains(config.m_hopType));
|
||||
const InterfaceConfig& lastConfig =
|
||||
m_connections.value(config.m_hopType).m_config;
|
||||
|
||||
// Configure routing for new excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
addExclusionRoute(IPAddress(i));
|
||||
}
|
||||
|
||||
// Activate the new peer and its routes.
|
||||
if (!wgutils()->updatePeer(config)) {
|
||||
logger.error() << "Server switch failed to update the wireguard interface";
|
||||
return false;
|
||||
}
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||
logger.error() << "Server switch failed to update the routing table";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove routing entries for the old peer.
|
||||
for (const QString& i : lastConfig.m_excludedAddresses) {
|
||||
delExclusionRoute(QHostAddress(i));
|
||||
}
|
||||
for (const IPAddress& ip : lastConfig.m_allowedIPAddressRanges) {
|
||||
if (!config.m_allowedIPAddressRanges.contains(ip)) {
|
||||
wgutils()->deleteRoutePrefix(ip);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old peer if it is no longer necessary.
|
||||
if (config.m_serverPublicKey != lastConfig.m_serverPublicKey) {
|
||||
if (!wgutils()->deletePeer(lastConfig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_connections[config.m_hopType] = ConnectionState(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject Daemon::getStatus() {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
QJsonObject json;
|
||||
logger.debug() << "Status request";
|
||||
|
||||
if (!wgutils()->interfaceExists() || m_connections.isEmpty()) {
|
||||
WireguardUtils* wg = primaryWgutils();
|
||||
if (!wg || !wg->interfaceExists() || !m_connections.contains(m_primaryIfname)) {
|
||||
json.insert("connected", QJsonValue(false));
|
||||
return json;
|
||||
}
|
||||
|
||||
const ConnectionState& connection = m_connections.first();
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
const ConnectionState& connection = m_connections.value(m_primaryIfname);
|
||||
QList<WireguardUtils::PeerStatus> peers = wg->getPeerStatus();
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (status.m_pubkey != connection.m_config.m_serverPublicKey) {
|
||||
continue;
|
||||
@@ -585,38 +584,50 @@ QJsonObject Daemon::getStatus() {
|
||||
return json;
|
||||
}
|
||||
|
||||
void Daemon::checkHandshake() {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
void Daemon::checkActivations() {
|
||||
const QDateTime now = QDateTime::currentDateTime();
|
||||
QStringList timedOut;
|
||||
bool anyPending = false;
|
||||
|
||||
logger.debug() << "Checking for handshake...";
|
||||
|
||||
int pendingHandshakes = 0;
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (ConnectionState& connection : m_connections) {
|
||||
const InterfaceConfig& config = connection.m_config;
|
||||
if (connection.m_date.isValid()) {
|
||||
continue;
|
||||
for (auto it = m_connections.begin(); it != m_connections.end(); ++it) {
|
||||
const QString& ifname = it.key();
|
||||
ConnectionState& cs = it.value();
|
||||
if (cs.m_date.isValid()) {
|
||||
continue; // already handshaked
|
||||
}
|
||||
logger.debug() << "awaiting" << config.m_serverPublicKey;
|
||||
logger.debug() << "awaiting" << cs.m_config.m_serverPublicKey;
|
||||
|
||||
// Check if the handshake has completed.
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (config.m_serverPublicKey != status.m_pubkey) {
|
||||
WireguardUtils* wg = m_tunnels.value(ifname);
|
||||
bool handshaked = false;
|
||||
if (wg) {
|
||||
for (const WireguardUtils::PeerStatus& status : wg->getPeerStatus()) {
|
||||
if (status.m_pubkey != cs.m_config.m_serverPublicKey) {
|
||||
continue;
|
||||
}
|
||||
if (status.m_handshake != 0) {
|
||||
connection.m_date.setMSecsSinceEpoch(status.m_handshake);
|
||||
emit connected(status.m_pubkey);
|
||||
cs.m_date.setMSecsSinceEpoch(status.m_handshake);
|
||||
emit tunnelConnected(ifname, status.m_pubkey);
|
||||
handshaked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handshaked) {
|
||||
continue;
|
||||
}
|
||||
if (cs.m_deadline.isValid() && now > cs.m_deadline) {
|
||||
timedOut.append(ifname);
|
||||
} else {
|
||||
anyPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connection.m_date.isValid()) {
|
||||
pendingHandshakes++;
|
||||
}
|
||||
for (const QString& ifname : timedOut) {
|
||||
logger.warning() << "Tunnel handshake timed out:" << m_tunnels.value(ifname)->interfaceName();
|
||||
emit tunnelHandshakeFailed(ifname);
|
||||
deactivateTunnel(ifname);
|
||||
}
|
||||
|
||||
// Check again if there were connections that haven't completed a handshake.
|
||||
if (pendingHandshakes > 0) {
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
if (!anyPending) {
|
||||
m_activationTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -22,7 +22,6 @@ class Daemon : public QObject {
|
||||
enum Op {
|
||||
Up,
|
||||
Down,
|
||||
Switch,
|
||||
};
|
||||
|
||||
explicit Daemon(QObject* parent);
|
||||
@@ -32,10 +31,15 @@ class Daemon : public QObject {
|
||||
|
||||
static bool parseConfig(const QJsonObject& obj, InterfaceConfig& config);
|
||||
|
||||
virtual bool activate(const InterfaceConfig& config);
|
||||
bool activate(const QString& ifname, const InterfaceConfig& config);
|
||||
bool setPrimary(const QString& ifname, const InterfaceConfig& config);
|
||||
bool deactivateTunnel(const QString& ifname);
|
||||
virtual bool deactivate(bool emitSignals = true);
|
||||
virtual QJsonObject getStatus();
|
||||
|
||||
const QString& primaryIfname() const { return m_primaryIfname; }
|
||||
WireguardUtils* wgutilsFor(const QString& ifname) const { return m_tunnels.value(ifname); }
|
||||
|
||||
// Callback before any Activating measure is done
|
||||
virtual void prepareActivation(const InterfaceConfig& config, int inetAdapterIndex = 0) {
|
||||
Q_UNUSED(config) };
|
||||
@@ -46,12 +50,8 @@ class Daemon : public QObject {
|
||||
void cleanLogs();
|
||||
|
||||
signals:
|
||||
void connected(const QString& pubkey);
|
||||
/**
|
||||
* Can be fired if a call to activate() was unsucessfull
|
||||
* and connected systems should rollback
|
||||
*/
|
||||
void activationFailure();
|
||||
void tunnelConnected(const QString& ifname, const QString& pubkey);
|
||||
void tunnelHandshakeFailed(const QString& ifname);
|
||||
void disconnected();
|
||||
void backendFailure(DaemonError reason = DaemonError::ERROR_FATAL);
|
||||
|
||||
@@ -59,6 +59,10 @@ class Daemon : public QObject {
|
||||
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;
|
||||
|
||||
protected:
|
||||
virtual bool run(Op op, const InterfaceConfig& config) {
|
||||
@@ -66,13 +70,11 @@ class Daemon : public QObject {
|
||||
Q_UNUSED(config);
|
||||
return true;
|
||||
}
|
||||
virtual bool supportServerSwitching(const InterfaceConfig& config) const;
|
||||
virtual bool switchServer(const InterfaceConfig& config);
|
||||
virtual WireguardUtils* wgutils() const = 0;
|
||||
virtual WireguardUtils* createWgUtils() = 0;
|
||||
virtual void replaceActiveWgUtils(WireguardUtils* newUtils) = 0;
|
||||
|
||||
WireguardUtils* m_stagingWgutils = nullptr;
|
||||
QMap<QString, WireguardUtils*> m_tunnels;
|
||||
QString m_primaryIfname;
|
||||
|
||||
virtual bool supportIPUtils() const { return false; }
|
||||
virtual IPUtils* iputils() { return nullptr; }
|
||||
virtual DnsUtils* dnsutils() { return nullptr; }
|
||||
@@ -80,18 +82,16 @@ class Daemon : public QObject {
|
||||
static bool parseStringList(const QJsonObject& obj, const QString& name,
|
||||
QStringList& list);
|
||||
|
||||
void checkHandshake();
|
||||
|
||||
class ConnectionState {
|
||||
public:
|
||||
ConnectionState(){};
|
||||
ConnectionState(const InterfaceConfig& config) { m_config = config; }
|
||||
QDateTime m_date;
|
||||
QDateTime m_deadline;
|
||||
InterfaceConfig m_config;
|
||||
};
|
||||
QMap<InterfaceConfig::HopType, ConnectionState> m_connections;
|
||||
QMap<QString, ConnectionState> m_connections;
|
||||
QHash<IPAddress, int> m_excludedAddrSet;
|
||||
QTimer m_handshakeTimer;
|
||||
};
|
||||
|
||||
#endif // DAEMON_H
|
||||
|
||||
@@ -31,8 +31,10 @@ DaemonLocalServerConnection::DaemonLocalServerConnection(QObject* parent,
|
||||
&DaemonLocalServerConnection::readData);
|
||||
|
||||
Daemon* daemon = Daemon::instance();
|
||||
connect(daemon, &Daemon::connected, this,
|
||||
&DaemonLocalServerConnection::connected);
|
||||
connect(daemon, &Daemon::tunnelConnected,
|
||||
this, &DaemonLocalServerConnection::onTunnelConnected);
|
||||
connect(daemon, &Daemon::tunnelHandshakeFailed,
|
||||
this, &DaemonLocalServerConnection::onTunnelHandshakeFailed);
|
||||
connect(daemon, &Daemon::disconnected, this,
|
||||
&DaemonLocalServerConnection::disconnected);
|
||||
connect(daemon, &Daemon::backendFailure, this,
|
||||
@@ -107,19 +109,44 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||
InterfaceConfig config;
|
||||
if (!Daemon::parseConfig(obj, config)) {
|
||||
logger.error() << "Invalid configuration";
|
||||
emit disconnected();
|
||||
disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Daemon::instance()->activate(config)) {
|
||||
if (!Daemon::instance()->activate(config.m_ifname, config)) {
|
||||
logger.error() << "Failed to activate the interface";
|
||||
emit disconnected();
|
||||
disconnected();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "deactivate") {
|
||||
const QString ifname = obj.value("ifname").toString();
|
||||
if (!ifname.isEmpty()) {
|
||||
Daemon::instance()->deactivateTunnel(ifname);
|
||||
} else {
|
||||
Daemon::instance()->deactivate(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "setPrimary") {
|
||||
InterfaceConfig config;
|
||||
if (!Daemon::parseConfig(obj, config)) {
|
||||
logger.error() << "setPrimary: invalid configuration";
|
||||
return;
|
||||
}
|
||||
if (!Daemon::instance()->setPrimary(config.m_ifname, config)) {
|
||||
logger.error() << "setPrimary failed";
|
||||
QJsonObject reply;
|
||||
reply.insert("type", "primaryFailed");
|
||||
reply.insert("ifname", config.m_ifname);
|
||||
write(reply);
|
||||
return;
|
||||
}
|
||||
QJsonObject reply;
|
||||
reply.insert("type", "primaryReady");
|
||||
reply.insert("ifname", config.m_ifname);
|
||||
write(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -146,10 +173,19 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||
logger.warning() << "Invalid command:" << type;
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::connected(const QString& pubkey) {
|
||||
void DaemonLocalServerConnection::onTunnelConnected(const QString& ifname,
|
||||
const QString& pubkey) {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "connected");
|
||||
obj.insert("pubkey", QJsonValue(pubkey));
|
||||
obj.insert("ifname", ifname);
|
||||
obj.insert("pubkey", pubkey);
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::onTunnelHandshakeFailed(const QString& ifname) {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "disconnected");
|
||||
obj.insert("ifname", ifname);
|
||||
write(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define DAEMONLOCALSERVERCONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "daemonerrors.h"
|
||||
|
||||
@@ -23,7 +24,8 @@ class DaemonLocalServerConnection final : public QObject {
|
||||
|
||||
void parseCommand(const QByteArray& json);
|
||||
|
||||
void connected(const QString& pubkey);
|
||||
void onTunnelConnected(const QString& ifname, const QString& pubkey);
|
||||
void onTunnelHandshakeFailed(const QString& ifname);
|
||||
void disconnected();
|
||||
void backendFailure(DaemonError err);
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
constexpr int ACTIVATION_TIMEOUT_MSEC = 30000;
|
||||
|
||||
class InterfaceConfig {
|
||||
Q_GADGET
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include "interfaceconfig.h"
|
||||
|
||||
constexpr const char* WG_INTERFACE = "amn0";
|
||||
|
||||
constexpr uint16_t WG_KEEPALIVE_PERIOD = 60;
|
||||
|
||||
class WireguardUtils : public QObject {
|
||||
@@ -35,7 +33,7 @@ class WireguardUtils : public QObject {
|
||||
virtual ~WireguardUtils() = default;
|
||||
|
||||
virtual bool interfaceExists() = 0;
|
||||
virtual QString interfaceName() { return WG_INTERFACE; }
|
||||
virtual QString interfaceName() = 0;
|
||||
virtual bool addInterface(const InterfaceConfig& config) = 0;
|
||||
virtual bool deleteInterface() = 0;
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ LinuxDaemon::LinuxDaemon() : Daemon(nullptr) {
|
||||
|
||||
logger.debug() << "Daemon created";
|
||||
|
||||
m_wgutils = new WireguardUtilsLinux(this);
|
||||
m_dnsutils = new DnsUtilsLinux(this);
|
||||
m_iputils = new IPUtilsLinux(this);
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#include "wireguardutilslinux.h"
|
||||
|
||||
class LinuxDaemon final : public Daemon {
|
||||
friend class IPUtilsMacos;
|
||||
|
||||
public:
|
||||
LinuxDaemon();
|
||||
~LinuxDaemon();
|
||||
@@ -23,7 +21,6 @@ class LinuxDaemon final : public Daemon {
|
||||
bool deactivate(bool emitSignals = true) override;
|
||||
|
||||
protected:
|
||||
WireguardUtils* wgutils() const override { return m_wgutils; }
|
||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||
bool supportIPUtils() const override { return true; }
|
||||
IPUtils* iputils() override { return m_iputils; }
|
||||
@@ -32,13 +29,7 @@ class LinuxDaemon final : public Daemon {
|
||||
return new WireguardUtilsLinux(this);
|
||||
}
|
||||
|
||||
void replaceActiveWgUtils(WireguardUtils* newUtils) override {
|
||||
delete m_wgutils;
|
||||
m_wgutils = static_cast<WireguardUtilsLinux*>(newUtils);
|
||||
}
|
||||
|
||||
private:
|
||||
WireguardUtilsLinux* m_wgutils = nullptr;
|
||||
DnsUtilsLinux* m_dnsutils = nullptr;
|
||||
IPUtilsLinux* m_iputils = nullptr;
|
||||
};
|
||||
|
||||
@@ -193,8 +193,8 @@ QStringList LinuxFirewall::getDNSRules(const QStringList& servers)
|
||||
QStringList result;
|
||||
for (const QString& server : servers)
|
||||
{
|
||||
result << QStringLiteral("-o amn0+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o amn0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o amn+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o amn+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun0+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun2+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
@@ -278,7 +278,7 @@ void LinuxFirewall::install()
|
||||
});
|
||||
|
||||
installAnchor(Both, QStringLiteral("200.allowVPN"), {
|
||||
QStringLiteral("-o amn0+ -j ACCEPT"),
|
||||
QStringLiteral("-o amn+ -j ACCEPT"),
|
||||
QStringLiteral("-o tun0+ -j ACCEPT"),
|
||||
QStringLiteral("-o tun2+ -j ACCEPT"),
|
||||
});
|
||||
|
||||
@@ -37,33 +37,6 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
// Descriptor for a set of firewall rules to be appled.
|
||||
//
|
||||
struct FirewallParams
|
||||
{
|
||||
QStringList dnsServers;
|
||||
QVector<QString> excludeApps; // Apps to exclude if VPN exemptions are enabled
|
||||
QStringList allowAddrs;
|
||||
QStringList blockAddrs;
|
||||
// The follow flags indicate which general rulesets are needed. Note that
|
||||
// this is after some sanity filtering, i.e. an allow rule may be listed
|
||||
// as not needed if there were no block rules preceding it. The rulesets
|
||||
// should be thought of as in last-match order.
|
||||
|
||||
bool blockAll; // Block all traffic by default
|
||||
bool allowVPN; // Exempt traffic through VPN tunnel
|
||||
bool allowDHCP; // Exempt DHCP traffic
|
||||
bool blockIPv6; // Block all IPv6 traffic
|
||||
bool allowLAN; // Exempt LAN traffic, including IPv6 LAN traffic
|
||||
bool blockDNS; // Block all DNS traffic except specified DNS servers
|
||||
bool allowPIA; // Exempt PIA executables
|
||||
bool allowLoopback; // Exempt loopback traffic
|
||||
bool allowHnsd; // Exempt Handshake DNS traffic
|
||||
bool allowVpnExemptions; // Exempt specified traffic from the tunnel (route it over the physical uplink instead)
|
||||
bool allowNets;
|
||||
bool blockNets;
|
||||
};
|
||||
|
||||
class LinuxFirewall
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -39,8 +39,6 @@ typedef struct wg_allowedip {
|
||||
struct wg_allowedip *next_allowedip;
|
||||
} wg_allowedip;
|
||||
|
||||
constexpr const char* WG_INTERFACE = "amn0";
|
||||
|
||||
static void nlmsg_append_attr(struct nlmsghdr* nlmsg, size_t maxlen,
|
||||
int attrtype, const void* attrdata,
|
||||
size_t attrlen);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
|
||||
#include "linuxfirewall.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
@@ -63,7 +62,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString ifname = config.m_ifname.isEmpty() ? QString(WG_INTERFACE) : config.m_ifname;
|
||||
const QString ifname = config.m_ifname;
|
||||
|
||||
QDir wgRuntimeDir(WG_RUNTIME_DIR);
|
||||
if (!wgRuntimeDir.exists()) {
|
||||
@@ -147,29 +146,6 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||
} else {
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_primaryDnsServer);
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
params.dnsServers.append(config.m_secondaryDnsServer);
|
||||
}
|
||||
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
}
|
||||
|
||||
return (err == 0);
|
||||
@@ -454,27 +430,3 @@ QString WireguardUtilsLinux::waitForTunnelName(const QString& filename) {
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params)
|
||||
{
|
||||
// double-check + ensure our firewall is installed and enabled
|
||||
if (!LinuxFirewall::isInstalled()) LinuxFirewall::install();
|
||||
|
||||
// Note: rule precedence is handled inside IpTablesFirewall
|
||||
LinuxFirewall::ensureRootAnchorPriority();
|
||||
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), params.blockAll);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), params.allowNets);
|
||||
LinuxFirewall::updateAllowNets(params.allowAddrs);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), params.blockNets);
|
||||
LinuxFirewall::updateBlockNets(params.blockAddrs);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
||||
LinuxFirewall::updateDNSServers(params.dnsServers);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "daemon/wireguardutils.h"
|
||||
#include "linuxroutemonitor.h"
|
||||
#include "linuxfirewall.h"
|
||||
|
||||
|
||||
class WireguardUtilsLinux final : public WireguardUtils {
|
||||
@@ -40,7 +39,6 @@ public:
|
||||
|
||||
bool excludeLocalNetworks(const QList<IPAddress>& lanAddressRanges) override;
|
||||
|
||||
void applyFirewallRules(FirewallParams& params);
|
||||
signals:
|
||||
void backendFailure();
|
||||
|
||||
|
||||
@@ -39,8 +39,12 @@ bool IPUtilsMacos::addInterfaceIPs(const InterfaceConfig& config) {
|
||||
}
|
||||
|
||||
bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
QString ifname = MacOSDaemon::instance()->m_wgutils->interfaceName();
|
||||
WireguardUtils* wg = MacOSDaemon::instance()->wgutilsFor(config.m_ifname);
|
||||
if (!wg) {
|
||||
logger.error() << "No wireguard interface for" << config.m_ifname;
|
||||
return false;
|
||||
}
|
||||
QString ifname = wg->interfaceName();
|
||||
struct ifreq ifr;
|
||||
|
||||
// Create socket file descriptor to perform the ioctl operations on
|
||||
@@ -80,8 +84,12 @@ bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) {
|
||||
}
|
||||
|
||||
bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
QString ifname = MacOSDaemon::instance()->m_wgutils->interfaceName();
|
||||
WireguardUtils* wg = MacOSDaemon::instance()->wgutilsFor(config.m_ifname);
|
||||
if (!wg) {
|
||||
logger.error() << "No wireguard interface for" << config.m_ifname;
|
||||
return false;
|
||||
}
|
||||
QString ifname = wg->interfaceName();
|
||||
struct ifaliasreq ifr;
|
||||
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifra_addr;
|
||||
struct sockaddr_in* ifrMask = (struct sockaddr_in*)&ifr.ifra_mask;
|
||||
@@ -130,8 +138,12 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||
}
|
||||
|
||||
bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
QString ifname = MacOSDaemon::instance()->m_wgutils->interfaceName();
|
||||
WireguardUtils* wg = MacOSDaemon::instance()->wgutilsFor(config.m_ifname);
|
||||
if (!wg) {
|
||||
logger.error() << "No wireguard interface for" << config.m_ifname;
|
||||
return false;
|
||||
}
|
||||
QString ifname = wg->interfaceName();
|
||||
struct in6_aliasreq ifr6;
|
||||
|
||||
// Name the interface and set family
|
||||
|
||||
@@ -29,7 +29,6 @@ MacOSDaemon::MacOSDaemon() : Daemon(nullptr) {
|
||||
|
||||
logger.debug() << "Daemon created";
|
||||
|
||||
m_wgutils = new WireguardUtilsMacos(this);
|
||||
m_dnsutils = new DnsUtilsMacos(this);
|
||||
m_iputils = new IPUtilsMacos(this);
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
#include "wireguardutilsmacos.h"
|
||||
|
||||
class MacOSDaemon final : public Daemon {
|
||||
friend class IPUtilsMacos;
|
||||
|
||||
public:
|
||||
MacOSDaemon();
|
||||
~MacOSDaemon();
|
||||
@@ -22,7 +20,6 @@ class MacOSDaemon final : public Daemon {
|
||||
bool deactivate(bool emitSignals = true) override;
|
||||
|
||||
protected:
|
||||
WireguardUtils* wgutils() const override { return m_wgutils; }
|
||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||
bool supportIPUtils() const override { return true; }
|
||||
IPUtils* iputils() override { return m_iputils; }
|
||||
@@ -31,13 +28,7 @@ class MacOSDaemon final : public Daemon {
|
||||
return new WireguardUtilsMacos(this);
|
||||
}
|
||||
|
||||
void replaceActiveWgUtils(WireguardUtils* newUtils) override {
|
||||
delete m_wgutils;
|
||||
m_wgutils = static_cast<WireguardUtilsMacos*>(newUtils);
|
||||
}
|
||||
|
||||
private:
|
||||
WireguardUtilsMacos* m_wgutils = nullptr;
|
||||
DnsUtilsMacos* m_dnsutils = nullptr;
|
||||
IPUtilsMacos* m_iputils = nullptr;
|
||||
};
|
||||
|
||||
@@ -36,35 +36,6 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
// Descriptor for a set of firewall rules to be appled.
|
||||
//
|
||||
struct FirewallParams
|
||||
{
|
||||
QStringList dnsServers;
|
||||
QVector<QString> excludeApps; // Apps to exclude if VPN exemptions are enabled
|
||||
|
||||
QStringList allowAddrs;
|
||||
QStringList blockAddrs;
|
||||
|
||||
// The follow flags indicate which general rulesets are needed. Note that
|
||||
// this is after some sanity filtering, i.e. an allow rule may be listed
|
||||
// as not needed if there were no block rules preceding it. The rulesets
|
||||
// should be thought of as in last-match order.
|
||||
|
||||
bool blockAll; // Block all traffic by default
|
||||
bool blockNets;
|
||||
bool allowNets;
|
||||
bool allowVPN; // Exempt traffic through VPN tunnel
|
||||
bool allowDHCP; // Exempt DHCP traffic
|
||||
bool blockIPv6; // Block all IPv6 traffic
|
||||
bool allowLAN; // Exempt LAN traffic, including IPv6 LAN traffic
|
||||
bool blockDNS; // Block all DNS traffic except specified DNS servers
|
||||
bool allowPIA; // Exempt PIA executables
|
||||
bool allowLoopback; // Exempt loopback traffic
|
||||
bool allowHnsd; // Exempt Handshake DNS traffic
|
||||
bool allowVpnExemptions; // Exempt specified traffic from the tunnel (route it over the physical uplink instead)
|
||||
};
|
||||
|
||||
class MacOSFirewall
|
||||
{
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString ifname = config.m_ifname.isEmpty() ? QString(WG_INTERFACE) : config.m_ifname;
|
||||
const QString ifname = config.m_ifname;
|
||||
|
||||
QDir wgRuntimeDir(WG_RUNTIME_DIR);
|
||||
if (!wgRuntimeDir.exists()) {
|
||||
@@ -146,30 +146,6 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||
} else {
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_primaryDnsServer);
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
params.dnsServers.append(config.m_secondaryDnsServer);
|
||||
}
|
||||
|
||||
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
}
|
||||
return (err == 0);
|
||||
}
|
||||
@@ -455,28 +431,3 @@ QString WireguardUtilsMacos::waitForTunnelName(const QString& filename) {
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void WireguardUtilsMacos::applyFirewallRules(FirewallParams& params)
|
||||
{
|
||||
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
||||
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
||||
if (!MacOSFirewall::isInstalled()) MacOSFirewall::install();
|
||||
|
||||
MacOSFirewall::ensureRootAnchorPriority();
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), params.blockAll);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), params.allowNets);
|
||||
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), params.allowNets,
|
||||
QStringLiteral("allownets"), params.allowAddrs);
|
||||
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), params.blockNets);
|
||||
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), params.blockNets,
|
||||
QStringLiteral("blocknets"), params.blockAddrs);
|
||||
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), params.dnsServers);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "daemon/wireguardutils.h"
|
||||
#include "macosroutemonitor.h"
|
||||
#include "macosfirewall.h"
|
||||
|
||||
class WireguardUtilsMacos final : public WireguardUtils {
|
||||
Q_OBJECT
|
||||
@@ -38,8 +37,6 @@ class WireguardUtilsMacos final : public WireguardUtils {
|
||||
|
||||
bool excludeLocalNetworks(const QList<IPAddress>& lanAddressRanges) override;
|
||||
|
||||
void applyFirewallRules(FirewallParams& params);
|
||||
|
||||
signals:
|
||||
void backendFailure();
|
||||
|
||||
|
||||
@@ -35,14 +35,8 @@ WindowsDaemon::WindowsDaemon() : Daemon(nullptr) {
|
||||
m_firewallManager = WindowsFirewall::create(this);
|
||||
Q_ASSERT(m_firewallManager != nullptr);
|
||||
|
||||
m_wgutils = WireguardUtilsWindows::create(m_firewallManager, this);
|
||||
m_dnsutils = new DnsUtilsWindows(this);
|
||||
m_splitTunnelManager = WindowsSplitTunnel::create(m_firewallManager);
|
||||
|
||||
connect(m_wgutils.get(), &WireguardUtilsWindows::backendFailure, this,
|
||||
&WindowsDaemon::monitorBackendFailure);
|
||||
connect(this, &WindowsDaemon::activationFailure,
|
||||
[this]() { m_firewallManager->disableKillSwitch(); });
|
||||
}
|
||||
|
||||
WindowsDaemon::~WindowsDaemon() {
|
||||
@@ -120,7 +114,3 @@ WireguardUtils* WindowsDaemon::createWgUtils() {
|
||||
&WindowsDaemon::monitorBackendFailure);
|
||||
return utils.release();
|
||||
}
|
||||
|
||||
void WindowsDaemon::replaceActiveWgUtils(WireguardUtils* newUtils) {
|
||||
m_wgutils.reset(static_cast<WireguardUtilsWindows*>(newUtils));
|
||||
}
|
||||
|
||||
@@ -28,10 +28,8 @@ class WindowsDaemon final : public Daemon {
|
||||
|
||||
protected:
|
||||
bool run(Op op, const InterfaceConfig& config) override;
|
||||
WireguardUtils* wgutils() const override { return m_wgutils.get(); }
|
||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||
WireguardUtils* createWgUtils() override;
|
||||
void replaceActiveWgUtils(WireguardUtils* newUtils) override;
|
||||
|
||||
private:
|
||||
void monitorBackendFailure();
|
||||
@@ -44,7 +42,6 @@ class WindowsDaemon final : public Daemon {
|
||||
|
||||
int m_inetAdapterIndex = -1;
|
||||
|
||||
std::unique_ptr<WireguardUtilsWindows> m_wgutils;
|
||||
DnsUtilsWindows* m_dnsutils = nullptr;
|
||||
std::unique_ptr<WindowsSplitTunnel> m_splitTunnelManager;
|
||||
QPointer<WindowsFirewall> m_firewallManager;
|
||||
|
||||
Reference in New Issue
Block a user