Merge PR #530: Add ping retry feature and provide it to the monitor

pull/542/head
jenkins 2022-06-30 15:04:34 +02:00
commit 68be029ede
10 changed files with 283 additions and 88 deletions

View File

@ -93,7 +93,7 @@ NetworkDeviceDiscoveryImpl::NetworkDeviceDiscoveryImpl(QObject *parent) :
NetworkDeviceDiscoveryImpl::~NetworkDeviceDiscoveryImpl()
{
delete m_cacheSettings;
}
NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
@ -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);
}
}
@ -201,34 +216,19 @@ void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *network
unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress()));
}
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address)
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries)
{
PingReply *reply = m_ping->ping(address, retries);
// Note: we use any ping used trough this method also for the monitor evaluation
PingReply *reply = m_ping->ping(address);
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);
}
}
});
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,7 +304,8 @@ void NetworkDeviceDiscoveryImpl::pingAllNetworkDevices()
if (targetAddress == entry.ip())
continue;
PingReply *reply = ping(targetAddress);
// 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);
@ -333,11 +334,37 @@ 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);
}
}
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();
@ -356,6 +383,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;
}
@ -391,16 +419,19 @@ 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)
{
if (!deviceInfo.isValid() || !deviceInfo.isComplete())
return;
m_cacheSettings->beginGroup("NetworkDeviceInfos");
m_cacheSettings->beginGroup(deviceInfo.macAddress());
m_cacheSettings->setValue("address", deviceInfo.address().toString());
@ -410,6 +441,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)
@ -432,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();
@ -449,15 +487,20 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monit
if (!requiresRefresh)
return;
if (monitor->networkDeviceInfo().address().isNull())
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);
});
}

View File

@ -71,7 +71,8 @@ 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;
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<PingReply *> m_runningPingRepies;
QHash<MacAddress, NetworkDeviceMonitorImpl *> m_monitors;
QHash<MacAddress, int> m_monitorsReferenceCount;
QHash<MacAddress, QDateTime> 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);

View File

@ -29,6 +29,9 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "networkdevicemonitorimpl.h"
#include "loggingcategories.h"
Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery)
namespace nymeaserver {
@ -41,7 +44,9 @@ NetworkDeviceMonitorImpl::NetworkDeviceMonitorImpl(const MacAddress &macAddress,
NetworkDeviceMonitorImpl::~NetworkDeviceMonitorImpl()
{
if (m_currentPingReply) {
m_currentPingReply->abort();
}
}
MacAddress NetworkDeviceMonitorImpl::macAddress() const
@ -73,6 +78,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 +98,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;

View File

@ -35,6 +35,7 @@
#include <QDateTime>
#include "network/networkdevicemonitor.h"
#include "network/pingreply.h"
namespace nymeaserver {
@ -44,7 +45,7 @@ class NetworkDeviceMonitorImpl : public NetworkDeviceMonitor
public:
explicit NetworkDeviceMonitorImpl(const MacAddress &macAddress, QObject *parent = nullptr);
~NetworkDeviceMonitorImpl();
~NetworkDeviceMonitorImpl() override;
MacAddress macAddress() const override;
@ -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;
};
}

View File

@ -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;

View File

@ -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);

View File

@ -111,11 +111,23 @@ 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);
PingReply *reply = createReply(hostAddress);
reply->m_retries = retries;
// Perform the reply in the next event loop to give the user time to do the reply connects
m_replyQueue.enqueue(reply);
sendNextReply();
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);
@ -133,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); });
}
@ -168,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));
@ -187,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));
@ -206,10 +213,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)
@ -287,12 +291,66 @@ 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);
});
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;
}
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_retries ||
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();
} else {
// 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++;
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
reply->m_timer->stop();
// Re-Enqueu the reply
m_replyQueue.enqueue(reply);
sendNextReply();
}
}
void Ping::onSocketReadyRead(int socketDescriptor)
@ -323,16 +381,19 @@ void Ping::onSocketReadyRead(int socketDescriptor)
<< "TTL" << ipHeader->ttl;
struct icmp *responsePacket = reinterpret_cast<struct icmp *>(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;
}
@ -344,8 +405,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;
}
@ -356,15 +417,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(dcPingTraffic()) << "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)
qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber
<< "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
@ -382,22 +449,25 @@ void Ping::onSocketReadyRead(int socketDescriptor)
<< "TTL" << ipHeader->ttl;
struct icmp *nestedResponsePacket = reinterpret_cast<struct icmp *>(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;
}

View File

@ -62,7 +62,8 @@ public:
PingReply::Error error() const;
PingReply *ping(const QHostAddress &hostAddress);
PingReply *ping(const QHostAddress &hostAddress, uint retries = 3);
PingReply *ping(const QHostAddress &hostAddress, bool lookupHost, uint retries = 3);
signals:
void availableChanged(bool available);
@ -76,6 +77,7 @@ private:
// Config
QByteArray m_payload = "ping from nymea";
PingReply::Error m_error = PingReply::ErrorNoError;
uint m_timeoutDuration = 5000;
// Socket
QSocketNotifier *m_socketNotifier = nullptr;
@ -98,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:

View File

@ -63,6 +63,16 @@ QNetworkInterface PingReply::networkInterface() const
return m_networkInterface;
}
uint PingReply::retries() const
{
return m_retries;
}
uint PingReply::retryCount() const
{
return m_retryCount;
}
double PingReply::duration() const
{
return m_duration;
@ -72,3 +82,15 @@ PingReply::Error PingReply::error() const
{
return m_error;
}
bool PingReply::doHostLookup() const
{
return m_doHostLookup;
}
void PingReply::abort()
{
m_timer->stop();
m_error = ErrorAborted;
emit aborted();
}

View File

@ -51,6 +51,7 @@ class LIBNYMEA_EXPORT PingReply : public QObject
public:
enum Error {
ErrorNoError,
ErrorAborted,
ErrorInvalidResponse,
ErrorNetworkDown,
ErrorNetworkUnreachable,
@ -70,22 +71,36 @@ public:
QString hostName() const;
QNetworkInterface networkInterface() const;
uint retries() const;
uint retryCount() const;
double duration() const;
Error error() const;
bool doHostLookup() const;
public slots:
void abort();
signals:
void finished();
void timeout();
void retry(Error error, uint retryCount);
void aborted();
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;
bool m_doHostLookup = false;
uint m_retries = 0;
uint m_retryCount = 0;
uint m_timeout = 3;
double m_duration = 0;
Error m_error = ErrorNoError;