Compare commits

...

7 Commits

Author SHA1 Message Date
NickVs2015 a53eb556b7 fix: : add support new structure 2026-04-30 17:30:59 +03:00
NickVs2015 fd915a4325 fix: replace m_linkDefaultRoutes map with single m_gatewayIfindex 2026-04-30 16:30:15 +03:00
NickVs2015 970fc26113 fix: solve infinite reconnect NM down 2026-04-30 16:30:14 +03:00
NickVs2015 c7b7ecd1d6 fix: killswitch compatible 2026-04-30 16:30:14 +03:00
NickVs2015 0edffc5f88 fix: NM down/up reconnection problem 2026-04-30 16:30:14 +03:00
NickVs2015 dcb097b0b2 fix: dns rewrites and flush 2026-04-30 16:30:14 +03:00
NickVs2015 a78f42a199 fix: make dbus async 2026-04-30 16:30:14 +03:00
6 changed files with 146 additions and 35 deletions
+15 -7
View File
@@ -286,7 +286,7 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
return { resGateway, QNetworkInterface::interfaceFromIndex(resIndex) }; return { resGateway, QNetworkInterface::interfaceFromIndex(resIndex) };
#endif #endif
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
constexpr int BUFFER_SIZE = 100; constexpr int BUFFER_SIZE = 8192;
int received_bytes = 0, msg_len = 0, route_attribute_len = 0; int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
int sock = -1, msgseq = 0; int sock = -1, msgseq = 0;
struct nlmsghdr *nlh, *nlmsg; struct nlmsghdr *nlh, *nlmsg;
@@ -294,7 +294,7 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
// This struct contain route attributes (route type) // This struct contain route attributes (route type)
struct rtattr *route_attribute; struct rtattr *route_attribute;
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE]; char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; char msgbuf[100], buffer[BUFFER_SIZE];
char *ptr = buffer; char *ptr = buffer;
struct timeval tv; struct timeval tv;
@@ -339,8 +339,8 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
nlh = (struct nlmsghdr *) ptr; nlh = (struct nlmsghdr *) ptr;
/* Check if the header is valid */ /* Check if the header is valid */
if((NLMSG_OK(nlmsg, received_bytes) == 0) || if((NLMSG_OK(nlh, received_bytes) == 0) ||
(nlmsg->nlmsg_type == NLMSG_ERROR)) (nlh->nlmsg_type == NLMSG_ERROR))
{ {
perror("Error in received packet"); perror("Error in received packet");
return {}; return {};
@@ -355,13 +355,15 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
} }
/* Break if its not a multi part message */ /* Break if its not a multi part message */
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) if ((nlh->nlmsg_flags & NLM_F_MULTI) == 0)
break; break;
} }
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); while ((nlh->nlmsg_seq != msgseq) || (nlh->nlmsg_pid != getpid()));
/* parse response */ /* parse response */
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) int remaining = msg_len + received_bytes;
nlh = (struct nlmsghdr *) buffer;
for ( ; NLMSG_OK(nlh, remaining); nlh = NLMSG_NEXT(nlh, remaining))
{ {
/* Get the route data */ /* Get the route data */
route_entry = (struct rtmsg *) NLMSG_DATA(nlh); route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
@@ -370,6 +372,10 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
if (route_entry->rtm_table != RT_TABLE_MAIN) if (route_entry->rtm_table != RT_TABLE_MAIN)
continue; continue;
/* Reset per-route to avoid cross-route state pollution */
memset(gateway_address, 0, sizeof(gateway_address));
memset(interface, 0, sizeof(interface));
route_attribute = (struct rtattr *) RTM_RTA(route_entry); route_attribute = (struct rtattr *) RTM_RTA(route_entry);
route_attribute_len = RTM_PAYLOAD(nlh); route_attribute_len = RTM_PAYLOAD(nlh);
@@ -395,6 +401,8 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
break; break;
} }
} }
if (!(*gateway_address) || !(*interface))
qDebug() << "getGatewayAndIface: no gateway found";
close(sock); close(sock);
return { gateway_address, QNetworkInterface::interfaceFromName(interface) }; return { gateway_address, QNetworkInterface::interfaceFromName(interface) };
#endif #endif
+93 -10
View File
@@ -7,8 +7,11 @@
#include <net/if.h> #include <net/if.h>
#include <QDBusVariant> #include <QDBusVariant>
#include <QNetworkInterface>
#include <QTimer>
#include <QtDBus/QtDBus> #include <QtDBus/QtDBus>
#include "core/utils/networkUtilities.h"
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
@@ -27,13 +30,45 @@ DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) {
logger.debug() << "DnsUtilsLinux created."; logger.debug() << "DnsUtilsLinux created.";
QDBusConnection conn = QDBusConnection::systemBus(); QDBusConnection conn = QDBusConnection::systemBus();
m_resolver = new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH, auto* watcher = new QDBusServiceWatcher(
DBUS_RESOLVE_MANAGER, conn, this); DBUS_RESOLVE_SERVICE, conn,
QDBusServiceWatcher::WatchForRegistration |
QDBusServiceWatcher::WatchForUnregistration, this);
connect(watcher, &QDBusServiceWatcher::serviceRegistered,
this, &DnsUtilsLinux::onResolverRegistered);
connect(watcher, &QDBusServiceWatcher::serviceUnregistered,
this, &DnsUtilsLinux::onResolverUnregistered);
if (conn.interface()->isServiceRegistered(DBUS_RESOLVE_SERVICE)) {
onResolverRegistered();
}
}
void DnsUtilsLinux::onResolverRegistered() {
m_resolver.reset(new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH,
DBUS_RESOLVE_MANAGER,
QDBusConnection::systemBus()));
logger.debug() << "systemd-resolved available, DNS resolver initialized";
if (!m_pendingIfname.isEmpty()) {
logger.debug() << "Re-applying DNS configuration for" << m_pendingIfname;
updateResolvers(m_pendingIfname, m_pendingResolvers);
}
}
void DnsUtilsLinux::onResolverUnregistered() {
logger.debug() << "systemd-resolved disappeared, dropping DNS resolver";
m_resolver.reset();
} }
DnsUtilsLinux::~DnsUtilsLinux() { DnsUtilsLinux::~DnsUtilsLinux() {
MZ_COUNT_DTOR(DnsUtilsLinux); MZ_COUNT_DTOR(DnsUtilsLinux);
if (m_resolver) {
if (m_gatewayIfindex > 0)
setLinkDefaultRoute(m_gatewayIfindex, true);
for (auto iterator = m_linkDomains.constBegin(); for (auto iterator = m_linkDomains.constBegin();
iterator != m_linkDomains.constEnd(); ++iterator) { iterator != m_linkDomains.constEnd(); ++iterator) {
QList<QVariant> argumentList; QList<QVariant> argumentList;
@@ -42,22 +77,41 @@ DnsUtilsLinux::~DnsUtilsLinux() {
m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"), m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"),
argumentList); argumentList);
} }
if (m_ifindex > 0) { if (m_ifindex > 0) {
m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex); m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex);
} }
}
logger.debug() << "DnsUtilsLinux destroyed."; logger.debug() << "DnsUtilsLinux destroyed.";
} }
bool DnsUtilsLinux::updateResolvers(const QString& ifname, bool DnsUtilsLinux::updateResolvers(const QString& ifname,
const QList<QHostAddress>& resolvers) { const QList<QHostAddress>& resolvers) {
if (m_gatewayIfindex > 0) {
setLinkDefaultRoute(m_gatewayIfindex, true);
m_gatewayIfindex = 0;
}
m_ifindex = if_nametoindex(qPrintable(ifname)); m_ifindex = if_nametoindex(qPrintable(ifname));
if (m_ifindex <= 0) { if (m_ifindex <= 0) {
logger.error() << "Unable to resolve ifindex for" << ifname; logger.error() << "Unable to resolve ifindex for" << ifname;
return false; return false;
} }
m_pendingIfname = ifname;
m_pendingResolvers = resolvers;
if (!m_resolver) {
logger.debug() << "systemd-resolved not ready, queuing DNS configuration";
return true;
}
const int gwIdx = NetworkUtilities::getGatewayAndIface().second.index();
if (gwIdx > 0 && gwIdx != m_ifindex && gwIdx != m_gatewayIfindex) {
m_gatewayIfindex = gwIdx;
setLinkDefaultRoute(gwIdx, false);
}
setLinkDNS(m_ifindex, resolvers); setLinkDNS(m_ifindex, resolvers);
setLinkDefaultRoute(m_ifindex, true); setLinkDefaultRoute(m_ifindex, true);
updateLinkDomains(); updateLinkDomains();
@@ -65,6 +119,14 @@ bool DnsUtilsLinux::updateResolvers(const QString& ifname,
} }
bool DnsUtilsLinux::restoreResolvers() { bool DnsUtilsLinux::restoreResolvers() {
m_pendingIfname.clear();
m_pendingResolvers.clear();
if (m_gatewayIfindex > 0) {
setLinkDefaultRoute(m_gatewayIfindex, true);
m_gatewayIfindex = 0;
}
for (auto iterator = m_linkDomains.constBegin(); for (auto iterator = m_linkDomains.constBegin();
iterator != m_linkDomains.constEnd(); ++iterator) { iterator != m_linkDomains.constEnd(); ++iterator) {
setLinkDomains(iterator.key(), iterator.value()); setLinkDomains(iterator.key(), iterator.value());
@@ -72,7 +134,7 @@ bool DnsUtilsLinux::restoreResolvers() {
m_linkDomains.clear(); m_linkDomains.clear();
/* Revert the VPN interface's DNS configuration */ /* Revert the VPN interface's DNS configuration */
if (m_ifindex > 0) { if (m_ifindex > 0 && m_resolver) {
QList<QVariant> argumentList = {QVariant::fromValue(m_ifindex)}; QList<QVariant> argumentList = {QVariant::fromValue(m_ifindex)};
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList( QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
QStringLiteral("RevertLink"), argumentList); QStringLiteral("RevertLink"), argumentList);
@@ -90,13 +152,14 @@ bool DnsUtilsLinux::restoreResolvers() {
void DnsUtilsLinux::dnsCallCompleted(QDBusPendingCallWatcher* call) { void DnsUtilsLinux::dnsCallCompleted(QDBusPendingCallWatcher* call) {
QDBusPendingReply<> reply = *call; QDBusPendingReply<> reply = *call;
if (reply.isError()) { if (reply.isError()) {
logger.error() << "Error received from the DBus service"; logger.debug() << "DBus call failed (may be transient after systemd-resolved restart)";
} }
delete call; delete call;
} }
void DnsUtilsLinux::setLinkDNS(int ifindex, void DnsUtilsLinux::setLinkDNS(int ifindex,
const QList<QHostAddress>& resolvers) { const QList<QHostAddress>& resolvers) {
if (!m_resolver) return;
QList<DnsResolver> resolverList; QList<DnsResolver> resolverList;
char ifnamebuf[IF_NAMESIZE]; char ifnamebuf[IF_NAMESIZE];
const char* ifname = if_indextoname(ifindex, ifnamebuf); const char* ifname = if_indextoname(ifindex, ifnamebuf);
@@ -121,6 +184,7 @@ void DnsUtilsLinux::setLinkDNS(int ifindex,
void DnsUtilsLinux::setLinkDomains(int ifindex, void DnsUtilsLinux::setLinkDomains(int ifindex,
const QList<DnsLinkDomain>& domains) { const QList<DnsLinkDomain>& domains) {
if (!m_resolver) return;
char ifnamebuf[IF_NAMESIZE]; char ifnamebuf[IF_NAMESIZE];
const char* ifname = if_indextoname(ifindex, ifnamebuf); const char* ifname = if_indextoname(ifindex, ifnamebuf);
if (ifname) { if (ifname) {
@@ -144,6 +208,7 @@ void DnsUtilsLinux::setLinkDomains(int ifindex,
} }
void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) { void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) {
if (!m_resolver) return;
QList<QVariant> argumentList; QList<QVariant> argumentList;
argumentList << QVariant::fromValue(ifindex); argumentList << QVariant::fromValue(ifindex);
argumentList << QVariant::fromValue(enable); argumentList << QVariant::fromValue(enable);
@@ -156,6 +221,7 @@ void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) {
} }
void DnsUtilsLinux::updateLinkDomains() { void DnsUtilsLinux::updateLinkDomains() {
if (!m_resolver) return;
/* Get the list of search domains, and remove any others that might conspire /* Get the list of search domains, and remove any others that might conspire
* to satisfy DNS resolution. Unfortunately, this is a pain because Qt doesn't * to satisfy DNS resolution. Unfortunately, this is a pain because Qt doesn't
* seem to be able to demarshall complex property types. * seem to be able to demarshall complex property types.
@@ -174,11 +240,20 @@ void DnsUtilsLinux::updateLinkDomains() {
void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) { void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) {
QDBusPendingReply<QVariant> reply = *call; QDBusPendingReply<QVariant> reply = *call;
call->deleteLater();
if (reply.isError()) { if (reply.isError()) {
logger.error() << "Error retrieving the DNS domains from the DBus service"; // systemd-resolved may still be starting up after a restart — retry a few times
delete call; if (m_ifindex > 0 && m_domainRetries++ < 5) {
logger.debug() << "systemd-resolved not ready yet, retrying DNS setup ("
<< m_domainRetries << "/5)";
QTimer::singleShot(500, this, &DnsUtilsLinux::updateLinkDomains);
} else {
logger.warning() << "Failed to configure DNS after 5 retries";
m_domainRetries = 0;
}
return; return;
} }
m_domainRetries = 0;
/* Update the state of the DNS domains */ /* Update the state of the DNS domains */
m_linkDomains.clear(); m_linkDomains.clear();
@@ -204,9 +279,17 @@ void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) {
} }
/* Add a root search domain for the new interface. */ /* Add a root search domain for the new interface. */
QList<DnsLinkDomain> newlist = {root}; if (m_ifindex > 0) {
setLinkDomains(m_ifindex, newlist); setLinkDomains(m_ifindex, {root});
delete call;
/* Disable DefaultRoute on the physical gateway so systemd-resolved
* routes all DNS through the VPN interface. */
const int gwIdx = NetworkUtilities::getGatewayAndIface().second.index();
if (gwIdx > 0 && gwIdx != m_ifindex && gwIdx != m_gatewayIfindex) {
m_gatewayIfindex = gwIdx;
setLinkDefaultRoute(gwIdx, false);
}
}
} }
static DnsMetatypeRegistrationProxy s_dnsMetatypeProxy; static DnsMetatypeRegistrationProxy s_dnsMetatypeProxy;
+12 -1
View File
@@ -6,7 +6,12 @@
#define DNSUTILSLINUX_H #define DNSUTILSLINUX_H
#include <QDBusInterface> #include <QDBusInterface>
#include <QScopedPointer>
#include <QDBusPendingCallWatcher> #include <QDBusPendingCallWatcher>
#include <QDBusServiceWatcher>
#include <QHostAddress>
#include <QList>
#include <QString>
#include "daemon/dnsutils.h" #include "daemon/dnsutils.h"
#include "dbustypeslinux.h" #include "dbustypeslinux.h"
@@ -29,13 +34,19 @@ class DnsUtilsLinux final : public DnsUtils {
void updateLinkDomains(); void updateLinkDomains();
private slots: private slots:
void onResolverRegistered();
void onResolverUnregistered();
void dnsCallCompleted(QDBusPendingCallWatcher*); void dnsCallCompleted(QDBusPendingCallWatcher*);
void dnsDomainsReceived(QDBusPendingCallWatcher*); void dnsDomainsReceived(QDBusPendingCallWatcher*);
private: private:
int m_ifindex = 0; int m_ifindex = 0;
int m_gatewayIfindex = 0;
int m_domainRetries = 0;
QMap<int, DnsLinkDomainList> m_linkDomains; QMap<int, DnsLinkDomainList> m_linkDomains;
QDBusInterface* m_resolver = nullptr; QScopedPointer<QDBusInterface> m_resolver;
QString m_pendingIfname;
QList<QHostAddress> m_pendingResolvers;
}; };
#endif // DNSUTILSLINUX_H #endif // DNSUTILSLINUX_H
@@ -237,7 +237,11 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
// Exclude the server address, except for multihop exit servers. // Exclude the server address, except for multihop exit servers.
if ((config.m_hopType != InterfaceConfig::MultiHopExit) && if ((config.m_hopType != InterfaceConfig::MultiHopExit) &&
(m_rtmonitor != nullptr)) { (m_rtmonitor != nullptr)) {
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); if (!config.m_serverIpv4AddrIn.isEmpty() &&
!m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn))) {
logger.error() << "No gateway — cannot add server exclusion route";
return false;
}
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
} }
+11 -6
View File
@@ -167,22 +167,27 @@ bool RouterLinux::flushDns()
//check what the dns manager use //check what the dns manager use
if (isServiceActive("nscd.service")) { if (isServiceActive("nscd.service")) {
qDebug() << "Restarting nscd.service"; qDebug() << "Flushing nscd cache";
p.start("systemctl", { "restart", "nscd" }); p.start("nscd", { "--invalidate=hosts" });
} else if (isServiceActive("systemd-resolved.service")) { } else if (isServiceActive("systemd-resolved.service")) {
qDebug() << "Restarting systemd-resolved.service"; qDebug() << "Flushing systemd-resolved DNS cache";
p.start("systemctl", { "restart", "systemd-resolved" }); p.start("resolvectl", { "flush-caches" });
} else { } else {
qDebug() << "No suitable DNS manager found."; qDebug() << "No suitable DNS manager found.";
return false; return false;
} }
p.waitForFinished(); p.waitForFinished();
QByteArray output(p.readAll()); QByteArray output = p.readAll();
if ((p.exitStatus() != QProcess::NormalExit) || (p.exitCode() != 0)) {
qDebug().noquote() << "Failed to flush DNS: " + output;
return false;
}
if (output.isEmpty()) if (output.isEmpty())
qDebug().noquote() << "Flush dns completed"; qDebug().noquote() << "Flush dns completed";
else else
qDebug().noquote() << "OUTPUT systemctl restart nscd/systemd-resolved: " + output; qDebug().noquote() << "OUTPUT dns flush: " + output;
return true; return true;
} }