mirror of https://github.com/nymea/nymea.git
Merge PR #530: Add ping retry feature and provide it to the monitor
commit
68be029ede
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue