From 2dbd8c47bac3f8ddf8ba8c3696c03d23c2ece51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 3 Jun 2022 16:38:07 +0200 Subject: [PATCH 1/6] Add ping retry feature and provide it to the monitor --- .../network/networkdevicediscoveryimpl.cpp | 22 ++++++-- .../network/networkdevicediscoveryimpl.h | 2 +- .../network/networkdevicemonitorimpl.cpp | 24 +++++++++ .../network/networkdevicemonitorimpl.h | 11 +++- libnymea/network/networkdevicediscovery.h | 2 +- libnymea/network/networkdevicemonitor.h | 3 ++ libnymea/network/ping.cpp | 51 +++++++++++++++---- libnymea/network/ping.h | 3 +- libnymea/network/pingreply.cpp | 17 +++++++ libnymea/network/pingreply.h | 11 ++++ 10 files changed, 127 insertions(+), 19 deletions(-) diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp index ed86dc82..9ffdb426 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp @@ -201,10 +201,10 @@ void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *network unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress())); } -PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address) +PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries) { // Note: we use any ping used trough this method also for the monitor evaluation - PingReply *reply = m_ping->ping(address); + PingReply *reply = m_ping->ping(address, retries); connect(reply, &PingReply::finished, this, [=](){ // Search cache for mac address and update last seen @@ -304,7 +304,8 @@ void NetworkDeviceDiscoveryImpl::pingAllNetworkDevices() if (targetAddress == entry.ip()) continue; - PingReply *reply = ping(targetAddress); + // Retry only once to ping a device + PingReply *reply = ping(targetAddress, 1); m_runningPingRepies.append(reply); connect(reply, &PingReply::finished, this, [=](){ m_runningPingRepies.removeAll(reply); @@ -333,7 +334,7 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw monitor->setLastSeen(QDateTime::currentDateTime()); monitor->setReachable(true); } else { - qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << reply->error(); + qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << "retrying" << reply->retries() << "times:" << reply->error(); monitor->setReachable(false); } } @@ -452,12 +453,23 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monit if (monitor->networkDeviceInfo().address().isNull()) return; + if (monitor->currentPingReply()) + return; + // Try to ping first qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString(); + PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address(), monitor->pingRetries()); + monitor->setCurrentPingReply(reply); monitor->setLastConnectionAttempt(currentDateTime); - PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address()); + connect(reply, &PingReply::retry, monitor, [=](PingReply::Error error, uint retryCount){ + Q_UNUSED(error) + Q_UNUSED(retryCount) + monitor->setLastConnectionAttempt(QDateTime::currentDateTime()); + }); + connect(reply, &PingReply::finished, monitor, [=](){ + monitor->setCurrentPingReply(nullptr); processMonitorPingResult(reply, monitor); }); } diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.h b/libnymea-core/hardware/network/networkdevicediscoveryimpl.h index d5ce9cb5..e8dc9de8 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.h +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.h @@ -71,7 +71,7 @@ public: void unregisterMonitor(const MacAddress &macAddress) override; void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override; - PingReply *ping(const QHostAddress &address) override; + PingReply *ping(const QHostAddress &address, uint retries = 3) override; MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override; MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) override; diff --git a/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp b/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp index a00f797f..b40bfe62 100644 --- a/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp @@ -29,6 +29,9 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "networkdevicemonitorimpl.h" +#include "loggingcategories.h" + +Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery) namespace nymeaserver { @@ -73,6 +76,7 @@ void NetworkDeviceMonitorImpl::setReachable(bool reachable) if (m_reachable == reachable) return; + qCDebug(dcNetworkDeviceDiscovery()) << this << (reachable ? "is now reachable" : "is not reachable any more."); m_reachable = reachable; emit reachableChanged(m_reachable); } @@ -92,6 +96,26 @@ void NetworkDeviceMonitorImpl::setLastSeen(const QDateTime &lastSeen) emit lastSeenChanged(m_lastSeen); } +uint NetworkDeviceMonitorImpl::pingRetries() const +{ + return m_pingRetries; +} + +void NetworkDeviceMonitorImpl::setPingRetries(uint pingRetries) +{ + m_pingRetries = pingRetries; +} + +PingReply *NetworkDeviceMonitorImpl::currentPingReply() const +{ + return m_currentPingReply; +} + +void NetworkDeviceMonitorImpl::setCurrentPingReply(PingReply *reply) +{ + m_currentPingReply = reply; +} + QDateTime NetworkDeviceMonitorImpl::lastConnectionAttempt() const { return m_lastConnectionAttempt; diff --git a/libnymea-core/hardware/network/networkdevicemonitorimpl.h b/libnymea-core/hardware/network/networkdevicemonitorimpl.h index 57720ebe..1363fb80 100644 --- a/libnymea-core/hardware/network/networkdevicemonitorimpl.h +++ b/libnymea-core/hardware/network/networkdevicemonitorimpl.h @@ -35,6 +35,7 @@ #include #include "network/networkdevicemonitor.h" +#include "network/pingreply.h" namespace nymeaserver { @@ -57,16 +58,24 @@ public: QDateTime lastSeen() const override; void setLastSeen(const QDateTime &lastSeen); + uint pingRetries() const override; + void setPingRetries(uint pingRetries) override; + + PingReply *currentPingReply() const; + void setCurrentPingReply(PingReply *reply); + QDateTime lastConnectionAttempt() const; void setLastConnectionAttempt(const QDateTime &lastConnectionAttempt); + private: NetworkDeviceInfo m_networkDeviceInfo; MacAddress m_macAddress; bool m_reachable = false; QDateTime m_lastSeen; QDateTime m_lastConnectionAttempt; - + uint m_pingRetries = 5; + PingReply *m_currentPingReply = nullptr; }; } diff --git a/libnymea/network/networkdevicediscovery.h b/libnymea/network/networkdevicediscovery.h index 422ae79f..c5450add 100644 --- a/libnymea/network/networkdevicediscovery.h +++ b/libnymea/network/networkdevicediscovery.h @@ -60,7 +60,7 @@ public: virtual void unregisterMonitor(const MacAddress &macAddress) = 0; virtual void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) = 0; - virtual PingReply *ping(const QHostAddress &address) = 0; + virtual PingReply *ping(const QHostAddress &address, uint retries = 3) = 0; virtual MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) = 0; virtual MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) = 0; diff --git a/libnymea/network/networkdevicemonitor.h b/libnymea/network/networkdevicemonitor.h index e42d8f22..dcd8f459 100644 --- a/libnymea/network/networkdevicemonitor.h +++ b/libnymea/network/networkdevicemonitor.h @@ -53,6 +53,9 @@ public: virtual bool reachable() const = 0; virtual QDateTime lastSeen() const = 0; + virtual uint pingRetries() const = 0; + virtual void setPingRetries(uint pingRetries) = 0; + signals: void reachableChanged(bool reachable); void lastSeenChanged(const QDateTime &lastSeen); diff --git a/libnymea/network/ping.cpp b/libnymea/network/ping.cpp index a16202c7..bb1638ff 100644 --- a/libnymea/network/ping.cpp +++ b/libnymea/network/ping.cpp @@ -111,11 +111,21 @@ PingReply::Error Ping::error() const return m_error; } -PingReply *Ping::ping(const QHostAddress &hostAddress) +PingReply *Ping::ping(const QHostAddress &hostAddress, uint retries) { PingReply *reply = new PingReply(this); reply->m_targetHostAddress = hostAddress; reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(hostAddress); + reply->m_reties = retries; + + connect(reply, &PingReply::timeout, this, [=](){ + // Note: this is not the ICMP timeout, here we actually got nothing from nobody... + finishReply(reply, PingReply::ErrorTimeout); + }); + + connect(reply, &PingReply::aborted, this, [=](){ + finishReply(reply, PingReply::ErrorAborted); + }); // Perform the reply in the next event loop to give the user time to do the reply connects m_replyQueue.enqueue(reply); @@ -206,10 +216,7 @@ void Ping::performPing(PingReply *reply) // Start reply timer and handle timeout m_pendingReplies.insert(reply->requestId(), reply); - reply->m_timer->start(8000); - connect(reply, &PingReply::timeout, this, [=](){ - finishReply(reply, PingReply::ErrorTimeout); - }); + reply->m_timer->start(m_timeoutDuration); } void Ping::verifyErrno(int error) @@ -289,10 +296,34 @@ quint16 Ping::calculateRequestId() void Ping::finishReply(PingReply *reply, PingReply::Error error) { - reply->m_error = error; - m_pendingReplies.remove(reply->requestId()); - emit reply->finished(); - reply->deleteLater(); + // Check if we should retry + if (reply->m_retryCount >= reply->m_reties || + error == PingReply::ErrorNoError || + error == PingReply::ErrorAborted || + error == PingReply::ErrorInvalidHostAddress || + error == PingReply::ErrorPermissionDenied) { + // No retry, we are done + reply->m_error = error; + reply->m_timer->stop(); + m_pendingReplies.remove(reply->requestId()); + emit reply->finished(); + reply->deleteLater(); + } else { + m_pendingReplies.remove(reply->requestId()); + reply->m_error = error; + reply->m_retryCount++; + reply->m_sequenceNumber++; + + qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString() << reply->m_retryCount << "/" << reply->m_reties; + emit reply->retry(error, reply->retryCount()); + + // Note: will be restarted once actually sent trough the network + reply->m_timer->stop(); + + // Re-Enqueu the reply + m_replyQueue.enqueue(reply); + sendNextReply(); + } } void Ping::onSocketReadyRead(int socketDescriptor) @@ -360,7 +391,7 @@ void Ping::onSocketReadyRead(int socketDescriptor) int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo))); m_pendingHostLookups.insert(lookupId, reply); - qCDebug(dcPingTraffic()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]" + qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]" << "ID:" << QString("0x%1").arg(responsePacket->icmp_id, 4, 16, QChar('0')) << "Sequence:" << htons(responsePacket->icmp_seq) << "Time:" << reply->duration() << "[ms]"; diff --git a/libnymea/network/ping.h b/libnymea/network/ping.h index 3ef536e2..dc7b4900 100644 --- a/libnymea/network/ping.h +++ b/libnymea/network/ping.h @@ -62,7 +62,7 @@ public: PingReply::Error error() const; - PingReply *ping(const QHostAddress &hostAddress); + PingReply *ping(const QHostAddress &hostAddress, uint retries = 3); signals: void availableChanged(bool available); @@ -76,6 +76,7 @@ private: // Config QByteArray m_payload = "ping from nymea"; PingReply::Error m_error = PingReply::ErrorNoError; + uint m_timeoutDuration = 5000; // Socket QSocketNotifier *m_socketNotifier = nullptr; diff --git a/libnymea/network/pingreply.cpp b/libnymea/network/pingreply.cpp index c3fa499e..8afb2752 100644 --- a/libnymea/network/pingreply.cpp +++ b/libnymea/network/pingreply.cpp @@ -63,6 +63,16 @@ QNetworkInterface PingReply::networkInterface() const return m_networkInterface; } +uint PingReply::retries() const +{ + return m_reties; +} + +uint PingReply::retryCount() const +{ + return m_retryCount; +} + double PingReply::duration() const { return m_duration; @@ -72,3 +82,10 @@ PingReply::Error PingReply::error() const { return m_error; } + +void PingReply::abort() +{ + m_timer->stop(); + m_error = ErrorAborted; + emit aborted(); +} diff --git a/libnymea/network/pingreply.h b/libnymea/network/pingreply.h index 8bf8ca19..72f8fcc4 100644 --- a/libnymea/network/pingreply.h +++ b/libnymea/network/pingreply.h @@ -51,6 +51,7 @@ class LIBNYMEA_EXPORT PingReply : public QObject public: enum Error { ErrorNoError, + ErrorAborted, ErrorInvalidResponse, ErrorNetworkDown, ErrorNetworkUnreachable, @@ -70,13 +71,21 @@ public: QString hostName() const; QNetworkInterface networkInterface() const; + uint retries() const; + uint retryCount() const; + double duration() const; Error error() const; +public slots: + void abort(); + signals: void finished(); void timeout(); + void retry(Error error, uint retryCount); + void aborted(); private: QTimer *m_timer = nullptr; @@ -86,6 +95,8 @@ private: QString m_hostName; QNetworkInterface m_networkInterface; + uint m_reties = 0; + uint m_retryCount = 0; uint m_timeout = 3; double m_duration = 0; Error m_error = ErrorNoError; From 568bbc6972098456509a8812ade12e8045f6ca0e Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 9 Jun 2022 00:03:14 +0200 Subject: [PATCH 2/6] Smaller fixes in the NetworkDiscovery Fixes a theoretical memory leak (m_cacheSettings wasn't delete) which isn't really an issue in practice but valgrind complains on it. Fixes a typo: m_reties -> m_retries --- .../hardware/network/networkdevicediscoveryimpl.cpp | 2 +- libnymea/network/ping.cpp | 6 +++--- libnymea/network/pingreply.cpp | 2 +- libnymea/network/pingreply.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp index 9ffdb426..dc088b6b 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp @@ -93,7 +93,7 @@ NetworkDeviceDiscoveryImpl::NetworkDeviceDiscoveryImpl(QObject *parent) : NetworkDeviceDiscoveryImpl::~NetworkDeviceDiscoveryImpl() { - + delete m_cacheSettings; } NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover() diff --git a/libnymea/network/ping.cpp b/libnymea/network/ping.cpp index bb1638ff..26f227dd 100644 --- a/libnymea/network/ping.cpp +++ b/libnymea/network/ping.cpp @@ -116,7 +116,7 @@ PingReply *Ping::ping(const QHostAddress &hostAddress, uint retries) PingReply *reply = new PingReply(this); reply->m_targetHostAddress = hostAddress; reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(hostAddress); - reply->m_reties = retries; + reply->m_retries = retries; connect(reply, &PingReply::timeout, this, [=](){ // Note: this is not the ICMP timeout, here we actually got nothing from nobody... @@ -297,7 +297,7 @@ quint16 Ping::calculateRequestId() void Ping::finishReply(PingReply *reply, PingReply::Error error) { // Check if we should retry - if (reply->m_retryCount >= reply->m_reties || + if (reply->m_retryCount >= reply->m_retries || error == PingReply::ErrorNoError || error == PingReply::ErrorAborted || error == PingReply::ErrorInvalidHostAddress || @@ -314,7 +314,7 @@ void Ping::finishReply(PingReply *reply, PingReply::Error error) reply->m_retryCount++; reply->m_sequenceNumber++; - qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString() << reply->m_retryCount << "/" << reply->m_reties; + qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString() << reply->m_retryCount << "/" << reply->m_retries; emit reply->retry(error, reply->retryCount()); // Note: will be restarted once actually sent trough the network diff --git a/libnymea/network/pingreply.cpp b/libnymea/network/pingreply.cpp index 8afb2752..f16db831 100644 --- a/libnymea/network/pingreply.cpp +++ b/libnymea/network/pingreply.cpp @@ -65,7 +65,7 @@ QNetworkInterface PingReply::networkInterface() const uint PingReply::retries() const { - return m_reties; + return m_retries; } uint PingReply::retryCount() const diff --git a/libnymea/network/pingreply.h b/libnymea/network/pingreply.h index 72f8fcc4..09fb0b65 100644 --- a/libnymea/network/pingreply.h +++ b/libnymea/network/pingreply.h @@ -95,7 +95,7 @@ private: QString m_hostName; QNetworkInterface m_networkInterface; - uint m_reties = 0; + uint m_retries = 0; uint m_retryCount = 0; uint m_timeout = 3; double m_duration = 0; From d1db6a3774d0fc70f1eb5440365fe5f0c2378259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 20 Jun 2022 13:10:14 +0200 Subject: [PATCH 3/6] Fix network device cache housekeeping and sync the settings --- libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp index dc088b6b..a5c1771f 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp @@ -357,6 +357,7 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache() if (lastSeen.date().addDays(m_cacheCleanupPeriod) < now.date()) { qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << mac.toString(); m_cacheSettings->remove(""); + m_cacheSettings->endGroup(); // mac address continue; } @@ -392,12 +393,12 @@ void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const MacAddress & m_networkInfoCache.remove(macAddress); m_lastSeen.remove(macAddress); - m_cacheSettings->beginGroup("NetworkDeviceInfos"); m_cacheSettings->beginGroup(macAddress.toString()); m_cacheSettings->remove(""); m_cacheSettings->endGroup(); // mac address m_cacheSettings->endGroup(); // NetworkDeviceInfos + m_cacheSettings->sync(); } void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo) @@ -411,6 +412,7 @@ void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo m_cacheSettings->setValue("lastSeen", convertMinuteBased(m_lastSeen.value(MacAddress(deviceInfo.macAddress()))).toMSecsSinceEpoch()); m_cacheSettings->endGroup(); // mac address m_cacheSettings->endGroup(); // NetworkDeviceInfos + m_cacheSettings->sync(); } void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo) From 0d20cf7816e6606a3f208b661daf3752da9ac99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 20 Jun 2022 16:58:05 +0200 Subject: [PATCH 4/6] Hold reference count of monitor objects Make host lookup optional Cleanup pending ping on monitor unregister --- .../network/networkdevicediscoveryimpl.cpp | 103 +++++++++++------- .../network/networkdevicediscoveryimpl.h | 4 + .../network/networkdevicemonitorimpl.cpp | 4 +- .../network/networkdevicemonitorimpl.h | 2 +- libnymea/network/ping.cpp | 56 +++++++--- libnymea/network/ping.h | 2 + libnymea/network/pingreply.cpp | 5 + libnymea/network/pingreply.h | 4 + 8 files changed, 126 insertions(+), 54 deletions(-) diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp index a5c1771f..a350667b 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp @@ -146,16 +146,21 @@ bool NetworkDeviceDiscoveryImpl::running() const NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(const MacAddress &macAddress) { - qCInfo(dcNetworkDeviceDiscovery()) << "Register new network device monitor for" << macAddress; - // Make sure we create only one monitor per MAC - if (m_monitors.contains(macAddress)) - return m_monitors.value(macAddress); - if (macAddress.isNull()) { qCWarning(dcNetworkDeviceDiscovery()) << "Could not register monitor for invalid" << macAddress; return nullptr; } + // Make sure we create only one monitor per MAC and keep track how many user + // have access to this monitor otherwise an unregister could cause a crash in + // an other plugin plugin which might still need it + if (m_monitors.contains(macAddress)) { + m_monitorsReferenceCount[macAddress] += 1; + qCInfo(dcNetworkDeviceDiscovery()) << "Register network device monitor for" << macAddress << "which already exists. Returning existing monitor having now" << m_monitorsReferenceCount[macAddress] << "references."; + return m_monitors.value(macAddress); + } + + qCInfo(dcNetworkDeviceDiscovery()) << "Register new network device monitor for" << macAddress; // Fill in cached information NetworkDeviceInfo info; @@ -168,6 +173,7 @@ NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(const MacAddre NetworkDeviceMonitorImpl *monitor = new NetworkDeviceMonitorImpl(macAddress, this); monitor->setNetworkDeviceInfo(info); m_monitors.insert(macAddress, monitor); + m_monitorsReferenceCount[macAddress] = 1; if (!available()) { qCWarning(dcNetworkDeviceDiscovery()) << "Registered monitor but the hardware resource is not available. The monitor will not work as expected" << monitor; @@ -189,10 +195,19 @@ NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(const MacAddre void NetworkDeviceDiscoveryImpl::unregisterMonitor(const MacAddress &macAddress) { + if (m_monitorsReferenceCount.contains(macAddress)) { + m_monitorsReferenceCount[macAddress] -= 1; + if (m_monitorsReferenceCount[macAddress] > 0) { + qCDebug(dcNetworkDeviceDiscovery()) << "Unregistered monitor for" << macAddress.toString() << "but keeping the monitor. There are still" << m_monitorsReferenceCount[macAddress] << "references to it."; + return; + } + } + if (m_monitors.contains(macAddress)) { NetworkDeviceMonitor *monitor = m_monitors.take(macAddress); qCInfo(dcNetworkDeviceDiscovery()) << "Unregister" << monitor; monitor->deleteLater(); + m_monitorsReferenceCount.remove(macAddress); } } @@ -203,32 +218,17 @@ void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *network PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries) { - // Note: we use any ping used trough this method also for the monitor evaluation PingReply *reply = m_ping->ping(address, retries); - connect(reply, &PingReply::finished, this, [=](){ - - // Search cache for mac address and update last seen - if (reply->error() == PingReply::ErrorNoError) { - foreach (const NetworkDeviceInfo &info, m_networkInfoCache) { - if (info.address() == address) { - // Found info for this ip, update the cache - MacAddress macAddress(info.macAddress()); - if (!macAddress.isNull() && m_networkInfoCache.contains(macAddress)) { - m_lastSeen[macAddress] = QDateTime::currentDateTime(); - saveNetworkDeviceCache(m_networkInfoCache.value(macAddress)); - } - } - } - } - - // Update any monitor - foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.values()) { - if (monitor->networkDeviceInfo().address() == address) { - processMonitorPingResult(reply, monitor); - } - } - }); + // Note: we use any ping used trough this method also for the monitor evaluation + watchPingReply(reply); + return reply; +} +PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, bool lookupHost, uint retries) +{ + PingReply *reply = m_ping->ping(address, lookupHost, retries); + // Note: we use any ping used trough this method also for the monitor evaluation + watchPingReply(reply); return reply; } @@ -304,8 +304,8 @@ void NetworkDeviceDiscoveryImpl::pingAllNetworkDevices() if (targetAddress == entry.ip()) continue; - // Retry only once to ping a device - PingReply *reply = ping(targetAddress, 1); + // Retry only once to ping a device and lookup the hostname on success + PingReply *reply = ping(targetAddress, true, 1); m_runningPingRepies.append(reply); connect(reply, &PingReply::finished, this, [=](){ m_runningPingRepies.removeAll(reply); @@ -339,6 +339,32 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw } } +void NetworkDeviceDiscoveryImpl::watchPingReply(PingReply *reply) +{ + connect(reply, &PingReply::finished, this, [=](){ + // Search cache for mac address and update last seen + if (reply->error() == PingReply::ErrorNoError) { + foreach (const NetworkDeviceInfo &info, m_networkInfoCache) { + if (info.address() == reply->targetHostAddress()) { + // Found info for this ip, update the cache + MacAddress macAddress(info.macAddress()); + if (!macAddress.isNull() && m_networkInfoCache.contains(macAddress)) { + m_lastSeen[macAddress] = QDateTime::currentDateTime(); + saveNetworkDeviceCache(m_networkInfoCache.value(macAddress)); + } + } + } + } + + // Update any monitor + foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.values()) { + if (monitor->networkDeviceInfo().address() == reply->targetHostAddress()) { + processMonitorPingResult(reply, monitor); + } + } + }); +} + void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache() { qCInfo(dcNetworkDeviceDiscovery()) << "Loading cached network device information from" << m_cacheSettings->fileName(); @@ -403,6 +429,9 @@ void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const MacAddress & void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo) { + if (!deviceInfo.isValid() || !deviceInfo.isComplete()) + return; + m_cacheSettings->beginGroup("NetworkDeviceInfos"); m_cacheSettings->beginGroup(deviceInfo.macAddress()); m_cacheSettings->setValue("address", deviceInfo.address().toString()); @@ -435,6 +464,12 @@ void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monitor) { + if (monitor->networkDeviceInfo().address().isNull()) + return; + + if (monitor->currentPingReply()) + return; + // Start action if we have not seen the device for gracePeriod seconds QDateTime currentDateTime = QDateTime::currentDateTime(); @@ -452,12 +487,6 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monit if (!requiresRefresh) return; - if (monitor->networkDeviceInfo().address().isNull()) - return; - - if (monitor->currentPingReply()) - return; - // Try to ping first qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString(); PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address(), monitor->pingRetries()); diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.h b/libnymea-core/hardware/network/networkdevicediscoveryimpl.h index e8dc9de8..36b082a5 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.h +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.h @@ -72,6 +72,7 @@ public: void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override; PingReply *ping(const QHostAddress &address, uint retries = 3) override; + PingReply *ping(const QHostAddress &address, bool lookupHost, uint retries = 3); MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override; MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) override; @@ -104,6 +105,7 @@ private: QList m_runningPingRepies; QHash m_monitors; + QHash m_monitorsReferenceCount; QHash m_lastSeen; QSettings *m_cacheSettings; @@ -113,6 +115,8 @@ private: void processMonitorPingResult(PingReply *reply, NetworkDeviceMonitorImpl *monitor); + void watchPingReply(PingReply *reply); + void loadNetworkDeviceCache(); void removeFromNetworkDeviceCache(const MacAddress &macAddress); void saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo); diff --git a/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp b/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp index b40bfe62..c0191ff2 100644 --- a/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicemonitorimpl.cpp @@ -44,7 +44,9 @@ NetworkDeviceMonitorImpl::NetworkDeviceMonitorImpl(const MacAddress &macAddress, NetworkDeviceMonitorImpl::~NetworkDeviceMonitorImpl() { - + if (m_currentPingReply) { + m_currentPingReply->abort(); + } } MacAddress NetworkDeviceMonitorImpl::macAddress() const diff --git a/libnymea-core/hardware/network/networkdevicemonitorimpl.h b/libnymea-core/hardware/network/networkdevicemonitorimpl.h index 1363fb80..e438d2d7 100644 --- a/libnymea-core/hardware/network/networkdevicemonitorimpl.h +++ b/libnymea-core/hardware/network/networkdevicemonitorimpl.h @@ -45,7 +45,7 @@ class NetworkDeviceMonitorImpl : public NetworkDeviceMonitor public: explicit NetworkDeviceMonitorImpl(const MacAddress &macAddress, QObject *parent = nullptr); - ~NetworkDeviceMonitorImpl(); + ~NetworkDeviceMonitorImpl() override; MacAddress macAddress() const override; diff --git a/libnymea/network/ping.cpp b/libnymea/network/ping.cpp index 26f227dd..b59a356a 100644 --- a/libnymea/network/ping.cpp +++ b/libnymea/network/ping.cpp @@ -113,19 +113,21 @@ PingReply::Error Ping::error() const PingReply *Ping::ping(const QHostAddress &hostAddress, uint retries) { - PingReply *reply = new PingReply(this); - reply->m_targetHostAddress = hostAddress; - reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(hostAddress); + PingReply *reply = createReply(hostAddress); reply->m_retries = retries; - connect(reply, &PingReply::timeout, this, [=](){ - // Note: this is not the ICMP timeout, here we actually got nothing from nobody... - finishReply(reply, PingReply::ErrorTimeout); - }); + // Perform the reply in the next event loop to give the user time to do the reply connects + m_replyQueue.enqueue(reply); + sendNextReply(); - connect(reply, &PingReply::aborted, this, [=](){ - finishReply(reply, PingReply::ErrorAborted); - }); + return reply; +} + +PingReply *Ping::ping(const QHostAddress &hostAddress, bool lookupHost, uint retries) +{ + PingReply *reply = createReply(hostAddress); + reply->m_retries = retries; + reply->m_doHostLookup = lookupHost; // Perform the reply in the next event loop to give the user time to do the reply connects m_replyQueue.enqueue(reply); @@ -143,7 +145,7 @@ void Ping::sendNextReply() return; PingReply *reply = m_replyQueue.dequeue(); - //qCDebug(dcPing()) << "Send next reply," << m_replyQueue.count() << "left in queue"; + qCDebug(dcPing()) << "Send next reply," << m_replyQueue.count() << "left in queue"; m_queueTimer->start(); QTimer::singleShot(0, reply, [=]() { performPing(reply); }); } @@ -294,6 +296,24 @@ quint16 Ping::calculateRequestId() return requestId; } +PingReply *Ping::createReply(const QHostAddress &hostAddress) +{ + PingReply *reply = new PingReply(this); + reply->m_targetHostAddress = hostAddress; + reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(hostAddress); + + connect(reply, &PingReply::timeout, this, [=](){ + // Note: this is not the ICMP timeout, here we actually got nothing from nobody... + finishReply(reply, PingReply::ErrorTimeout); + }); + + connect(reply, &PingReply::aborted, this, [=](){ + finishReply(reply, PingReply::ErrorAborted); + }); + + return reply; +} + void Ping::finishReply(PingReply *reply, PingReply::Error error) { // Check if we should retry @@ -387,15 +407,21 @@ void Ping::onSocketReadyRead(int socketDescriptor) timeValueSubtract(&receiveTimeValue, &reply->m_startTime); reply->m_duration = qRound((receiveTimeValue.tv_sec * 1000 + (double)receiveTimeValue.tv_usec / 1000) * 100) / 100.0; - // Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here - int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo))); - m_pendingHostLookups.insert(lookupId, reply); - qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]" << "ID:" << QString("0x%1").arg(responsePacket->icmp_id, 4, 16, QChar('0')) << "Sequence:" << htons(responsePacket->icmp_seq) << "Time:" << reply->duration() << "[ms]"; + if (reply->doHostLookup()) { + // Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here + int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo))); + m_pendingHostLookups.insert(lookupId, reply); + } else { + finishReply(reply, PingReply::ErrorNoError); + } + + + } else if (responsePacket->icmp_type == ICMP_DEST_UNREACH) { // Get the sending package diff --git a/libnymea/network/ping.h b/libnymea/network/ping.h index dc7b4900..42f26cea 100644 --- a/libnymea/network/ping.h +++ b/libnymea/network/ping.h @@ -63,6 +63,7 @@ public: PingReply::Error error() const; PingReply *ping(const QHostAddress &hostAddress, uint retries = 3); + PingReply *ping(const QHostAddress &hostAddress, bool lookupHost, uint retries = 3); signals: void availableChanged(bool available); @@ -99,6 +100,7 @@ private: void timeValueSubtract(struct timeval *start, struct timeval *stop); quint16 calculateRequestId(); + PingReply *createReply(const QHostAddress &hostAddress); void finishReply(PingReply *reply, PingReply::Error error); private slots: diff --git a/libnymea/network/pingreply.cpp b/libnymea/network/pingreply.cpp index f16db831..42ca099a 100644 --- a/libnymea/network/pingreply.cpp +++ b/libnymea/network/pingreply.cpp @@ -83,6 +83,11 @@ PingReply::Error PingReply::error() const return m_error; } +bool PingReply::doHostLookup() const +{ + return m_doHostLookup; +} + void PingReply::abort() { m_timer->stop(); diff --git a/libnymea/network/pingreply.h b/libnymea/network/pingreply.h index 09fb0b65..530a55f7 100644 --- a/libnymea/network/pingreply.h +++ b/libnymea/network/pingreply.h @@ -78,6 +78,8 @@ public: Error error() const; + bool doHostLookup() const; + public slots: void abort(); @@ -95,6 +97,8 @@ private: QString m_hostName; QNetworkInterface m_networkInterface; + bool m_doHostLookup = false; + uint m_retries = 0; uint m_retryCount = 0; uint m_timeout = 3; From c398f7ab2ff5640d5c0b9df445d7ee126c24ba2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 29 Jun 2022 12:25:09 +0200 Subject: [PATCH 5/6] Fix ping requestId and sequence number endiness from and to the network --- libnymea/network/ping.cpp | 51 ++++++++++++++++++------------------ libnymea/network/pingreply.h | 2 +- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/libnymea/network/ping.cpp b/libnymea/network/ping.cpp index b59a356a..644f0eb1 100644 --- a/libnymea/network/ping.cpp +++ b/libnymea/network/ping.cpp @@ -180,11 +180,10 @@ void Ping::performPing(PingReply *reply) memset(&requestPacket, 0, sizeof(requestPacket)); requestPacket.icmpHeadr.type = ICMP_ECHO; if (reply->requestId() == 0) { - requestPacket.icmpHeadr.un.echo.id = calculateRequestId(); - } else { - requestPacket.icmpHeadr.un.echo.id = reply->requestId(); + reply->m_requestId = calculateRequestId(); } - requestPacket.icmpHeadr.un.echo.sequence = htons(reply->m_sequenceNumber++); + requestPacket.icmpHeadr.un.echo.id = htons(reply->requestId()); + requestPacket.icmpHeadr.un.echo.sequence = htons(reply->sequenceNumber()); // Write the ICMP payload memset(&requestPacket.icmpPayload, ' ', sizeof(requestPacket.icmpPayload)); @@ -199,13 +198,9 @@ void Ping::performPing(PingReply *reply) qCWarning(dcPing()) << "Failed to get start time for ping measurement" << strerror(errno); } - reply->m_requestId = requestPacket.icmpHeadr.un.echo.id; - reply->m_targetHostAddress = targetHostAddress; - reply->m_sequenceNumber = requestPacket.icmpHeadr.un.echo.sequence; - qCDebug(dcPingTraffic()) << "Send ICMP echo request" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]" - << "ID:" << QString("0x%1").arg(requestPacket.icmpHeadr.un.echo.id, 4, 16, QChar('0')) - << "Sequence:" << htons(requestPacket.icmpHeadr.un.echo.sequence); + << "ID:" << QString("0x%1").arg(reply->requestId(), 4, 16, QChar('0')) + << "Sequence:" << reply->sequenceNumber(); // Send packet to the target ip int bytesSent = sendto(m_socketDescriptor, &requestPacket, sizeof(requestPacket), 0, (struct sockaddr *)&pingAddress, sizeof(pingAddress)); @@ -374,16 +369,19 @@ void Ping::onSocketReadyRead(int socketDescriptor) << "TTL" << ipHeader->ttl; struct icmp *responsePacket = reinterpret_cast(receiveBuffer + ipHeaderLength); + quint16 icmpId = htons(responsePacket->icmp_id); + quint16 icmpSequnceNumber = htons(responsePacket->icmp_seq); qCDebug(dcPingTraffic()) << "ICMP packt (Size:" << icmpPacketSize << "Bytes):" << "Type" << responsePacket->icmp_type << "Code:" << responsePacket->icmp_code - << "ID:" << QString("0x%1").arg(responsePacket->icmp_id, 4, 16, QChar('0')) - << "Sequence:" << responsePacket->icmp_seq; + << "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0')) + << "Sequence:" << icmpSequnceNumber; if (responsePacket->icmp_type == ICMP_ECHOREPLY) { - PingReply *reply = m_pendingReplies.take(responsePacket->icmp_id); + + PingReply *reply = m_pendingReplies.take(icmpId); if (!reply) { - qCDebug(dcPing()) << "No pending reply for ping echo response with id" << QString("0x%1").arg(responsePacket->icmp_id, 4, 16, QChar('0')) << "Sequence:" << htons(responsePacket->icmp_seq) << "from" << senderAddress.toString(); + qCDebug(dcPing()) << "No pending reply for ping echo response with id" << QString("0x%1").arg(icmpId, 4, 16, QChar('0')) << "Sequence:" << icmpSequnceNumber << "from" << senderAddress.toString(); return; } @@ -395,8 +393,8 @@ void Ping::onSocketReadyRead(int socketDescriptor) } // Verify sequence number - if (responsePacket->icmp_seq != reply->sequenceNumber()) { - qCWarning(dcPing()) << "Received echo reply with different sequence number" << htons(responsePacket->icmp_seq); + if (icmpSequnceNumber != reply->sequenceNumber()) { + qCWarning(dcPing()) << "Received echo reply with different sequence number" << icmpSequnceNumber; finishReply(reply, PingReply::ErrorInvalidResponse); return; } @@ -408,8 +406,8 @@ void Ping::onSocketReadyRead(int socketDescriptor) reply->m_duration = qRound((receiveTimeValue.tv_sec * 1000 + (double)receiveTimeValue.tv_usec / 1000) * 100) / 100.0; qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]" - << "ID:" << QString("0x%1").arg(responsePacket->icmp_id, 4, 16, QChar('0')) - << "Sequence:" << htons(responsePacket->icmp_seq) + << "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0')) + << "Sequence:" << icmpSequnceNumber << "Time:" << reply->duration() << "[ms]"; if (reply->doHostLookup()) { @@ -439,22 +437,25 @@ void Ping::onSocketReadyRead(int socketDescriptor) << "TTL" << ipHeader->ttl; struct icmp *nestedResponsePacket = reinterpret_cast(receiveBuffer + messageOffset + nestedIpHeaderLength); + icmpId = htons(nestedResponsePacket->icmp_id); + icmpSequnceNumber = htons(nestedResponsePacket->icmp_seq); + qCDebug(dcPingTraffic()) << "++ ICMP packt (Size:" << nestedIcmpPacketSize << "Bytes):" << "Type" << nestedResponsePacket->icmp_type << "Code:" << nestedResponsePacket->icmp_code - << "ID:" << QString("0x%1").arg(nestedResponsePacket->icmp_id, 4, 16, QChar('0')) - << "Sequence:" << nestedResponsePacket->icmp_seq; + << "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0')) + << "Sequence:" << icmpSequnceNumber; qCDebug(dcPing()) << "ICMP destination unreachable" << nestedDestinationAddress.toString() << "Code:" << nestedResponsePacket->icmp_code - << "ID:" << QString("0x%1").arg(nestedResponsePacket->icmp_id, 4, 16, QChar('0')) - << "Sequence:" << htons(nestedResponsePacket->icmp_seq); + << "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0')) + << "Sequence:" << icmpSequnceNumber; - PingReply *reply = m_pendingReplies.take(nestedResponsePacket->icmp_id); + PingReply *reply = m_pendingReplies.take(icmpId); if (!reply) { qCDebug(dcPingTraffic()) << "No pending reply for ping echo response unreachable with ID" - << QString("0x%1").arg(nestedResponsePacket->icmp_id, 4, 16, QChar('0')) - << "Sequence:" << htons(nestedResponsePacket->icmp_seq) + << QString("0x%1").arg(icmpId, 4, 16, QChar('0')) + << "Sequence:" << icmpSequnceNumber << "from" << nestedSenderAddress.toString() << "to" << nestedDestinationAddress.toString(); return; } diff --git a/libnymea/network/pingreply.h b/libnymea/network/pingreply.h index 530a55f7..0276d824 100644 --- a/libnymea/network/pingreply.h +++ b/libnymea/network/pingreply.h @@ -92,7 +92,7 @@ signals: private: QTimer *m_timer = nullptr; QHostAddress m_targetHostAddress; - quint16 m_sequenceNumber = 0; + quint16 m_sequenceNumber = 1; quint16 m_requestId = 0; QString m_hostName; QNetworkInterface m_networkInterface; From 6bb36a773bf0c8468c0eecc1705d71ccc5ffd31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 29 Jun 2022 12:47:55 +0200 Subject: [PATCH 6/6] Make sure the ping replies get cleaned up from queues --- libnymea/network/ping.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/libnymea/network/ping.cpp b/libnymea/network/ping.cpp index 644f0eb1..ec8aa1cd 100644 --- a/libnymea/network/ping.cpp +++ b/libnymea/network/ping.cpp @@ -306,6 +306,14 @@ PingReply *Ping::createReply(const QHostAddress &hostAddress) finishReply(reply, PingReply::ErrorAborted); }); + connect(reply, &PingReply::finished, this, [=](){ + reply->deleteLater(); + + // Cleanup any retry left over queue stuff + m_pendingReplies.remove(reply->requestId()); + m_replyQueue.removeAll(reply); + }); + return reply; } @@ -322,14 +330,18 @@ void Ping::finishReply(PingReply *reply, PingReply::Error error) reply->m_timer->stop(); m_pendingReplies.remove(reply->requestId()); emit reply->finished(); - reply->deleteLater(); } else { - m_pendingReplies.remove(reply->requestId()); + // Note: don't remove from m_pendingReplies to prevent + // double assignmet of request id's between 2 retries reply->m_error = error; reply->m_retryCount++; reply->m_sequenceNumber++; - qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString() << reply->m_retryCount << "/" << reply->m_retries; + if (reply->m_retries > 1) { + qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString() << reply->m_retryCount << "/" << reply->m_retries; + } else { + qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString(); + } emit reply->retry(error, reply->retryCount()); // Note: will be restarted once actually sent trough the network