diff --git a/networkdetector/broadcastping.cpp b/networkdetector/broadcastping.cpp new file mode 100644 index 00000000..9fec3c1e --- /dev/null +++ b/networkdetector/broadcastping.cpp @@ -0,0 +1,51 @@ +#include "broadcastping.h" +#include "extern-plugininfo.h" + +#include +#include + +BroadcastPing::BroadcastPing(QObject *parent) : QObject(parent) +{ + +} + +void BroadcastPing::run() +{ + qDeleteAll(m_runningPings.keys()); + m_runningPings.clear(); + + foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { + if (!interface.flags().testFlag(QNetworkInterface::IsUp) || !interface.flags().testFlag(QNetworkInterface::CanBroadcast) || interface.flags().testFlag(QNetworkInterface::IsLoopBack)) { + continue; + } + foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) { + if (addressEntry.broadcast().isNull()) { + continue; + } + qCDebug(dcNetworkDetector()) << "Sending Broadcast Ping on" << addressEntry.broadcast().toString() + '/' + QString::number(addressEntry.prefixLength()) + "..."; + QProcess *p = new QProcess(this); + m_runningPings.insert(p, addressEntry); + p->start("fping", {"-a", "-c", "1", "-g", addressEntry.broadcast().toString() + "/" + QString::number(addressEntry.prefixLength())}); + connect(p, SIGNAL(finished(int)), this, SLOT(broadcastPingFinished(int))); + } + } + if (m_runningPings.isEmpty()) { + qCWarning(dcNetworkDetector()) << "Cound not find any suitable interface for broadcast pinging"; + emit finished(); + } +} + +void BroadcastPing::broadcastPingFinished(int exitCode) +{ + Q_UNUSED(exitCode); + QProcess *p = static_cast(sender()); + QNetworkAddressEntry addressEntry = m_runningPings.take(p); + qCDebug(dcNetworkDetector()) << "Broadcast ping finished for network" << addressEntry.broadcast().toString() + '/' + QString::number(addressEntry.prefixLength()); +// qCDebug(dcNetworkDetector()) << p->readAllStandardError(); + p->deleteLater(); + + if (m_runningPings.isEmpty()) { + qCDebug(dcNetworkDetector()) << "All broadcast pings finished"; + emit finished(); + } +} diff --git a/networkdetector/broadcastping.h b/networkdetector/broadcastping.h new file mode 100644 index 00000000..b41f376f --- /dev/null +++ b/networkdetector/broadcastping.h @@ -0,0 +1,28 @@ +#ifndef BROADCASTPING_H +#define BROADCASTPING_H + +#include +#include +#include +#include + +class BroadcastPing : public QObject +{ + Q_OBJECT +public: + explicit BroadcastPing(QObject *parent = nullptr); + +signals: + void finished(); + +public slots: + void run(); + +private slots: + void broadcastPingFinished(int exitCode); + +private: + QHash m_runningPings; +}; + +#endif // BROADCASTPING_H diff --git a/networkdetector/devicemonitor.cpp b/networkdetector/devicemonitor.cpp index 18c3a9f8..5caa17bd 100644 --- a/networkdetector/devicemonitor.cpp +++ b/networkdetector/devicemonitor.cpp @@ -62,7 +62,13 @@ void DeviceMonitor::arpLookupFinished(int exitCode) line.replace(QRegExp("[ ]{1,}"), " "); QStringList parts = line.split(" "); int lladdrIndex = parts.indexOf("lladdr"); - if (lladdrIndex >= 0 && parts.count() > lladdrIndex + 1 && parts.at(lladdrIndex+1).toLower() == m_macAddress.toLower()) { + if (lladdrIndex == -1 || parts.count() <= lladdrIndex) { + continue; + } + QString entryIP = parts.first(); + QString entryMAC = parts.at(lladdrIndex + 1); + + if (entryMAC.toLower() == m_macAddress.toLower()) { found = true; QString entryIP = parts.first(); if (parts.last() == "REACHABLE") { @@ -72,13 +78,13 @@ void DeviceMonitor::arpLookupFinished(int exitCode) emit reachableChanged(true); } emit seen(); + m_lastSeenTime = QDateTime::currentDateTime(); // Verify if IP address is still the same if (entryIP != mostRecentIP) { mostRecentIP = entryIP; } // If we have a reachable entry, stop processing here needsPing = false; - m_failedPings = 0; break; } else { // ARP claims the device to be stale... Flagging device to require a ping. @@ -94,6 +100,11 @@ void DeviceMonitor::arpLookupFinished(int exitCode) } } } + } else if (entryIP == m_ipAddress) { + warn("There seems to be a device with our IP but different MAC. Resetting IP config."); + if (mostRecentIP == m_ipAddress) { + mostRecentIP.clear(); + } } } if (mostRecentIP != m_ipAddress) { @@ -101,6 +112,14 @@ void DeviceMonitor::arpLookupFinished(int exitCode) m_ipAddress = mostRecentIP; emit addressChanged(mostRecentIP); } + if (m_ipAddress.isEmpty()) { + warn("Device not found in ARP cache and no IP config available. Marking as gone."); + if (m_reachable) { + m_reachable = false; + emit reachableChanged(false); + } + return; + } if (!found) { log("Device not found in ARP cache."); arping(); @@ -144,13 +163,13 @@ void DeviceMonitor::arpingFinished(int exitCode) emit reachableChanged(true); } emit seen(); - m_failedPings = 0; + m_lastSeenTime = QDateTime::currentDateTime(); } else { log("ARP Ping failed."); ping(); } // read data to discard it from socket - QString data = QString::fromLatin1(m_pingProcess->readAll()); + QString data = QString::fromLatin1(m_arpingProcess->readAll()); Q_UNUSED(data) // qCDebug(dcNetworkDetector()) << "have ping data" << data; } @@ -172,16 +191,11 @@ void DeviceMonitor::pingFinished(int exitCode) emit reachableChanged(true); } emit seen(); - m_failedPings = 0; + m_lastSeenTime = QDateTime::currentDateTime(); } else { - m_failedPings++; - log("ICMP Ping failed for " + QString::number(m_failedPings) + " times. (Grace Period: " + QString::number(m_gracePeriod) + ")"); - if (m_failedPings > m_gracePeriod - 1) { - // Regular ping fails too... Fire a broadcast ping in case the device has changed IP. After that, our ARP table should be up to date again. - broadcastPing(); - } - if (m_failedPings > m_gracePeriod && m_reachable) { - log("Marking device as offline."); + log("ICMP Ping failed."); + if (m_reachable && m_lastSeenTime.addSecs(m_gracePeriod * 60) < QDateTime::currentDateTime()) { + log("Exceeded grace period of " + QString::number(m_gracePeriod) + " minutes. Marking device as offline."); m_reachable = false; emit reachableChanged(false); } @@ -192,41 +206,6 @@ void DeviceMonitor::pingFinished(int exitCode) // qCDebug(dcNetworkDetector()) << "have ping data" << data; } -void DeviceMonitor::broadcastPing() -{ - QNetworkAddressEntry targetAddressEntry; - foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { - foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) { - QHostAddress clientAddress(m_ipAddress); - if (clientAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) { - targetAddressEntry = addressEntry; - break; - } - } - } - if (targetAddressEntry.broadcast().isNull()) { - warn("Could not find a suitable broadcast address Ping."); - if (m_reachable) { - m_reachable = false; - emit reachableChanged(false); - } - return; - } - - QProcess *p = new QProcess(this); - log("Sending Broadcast Ping on " + targetAddressEntry.ip().toString() + '/' + QString::number(targetAddressEntry.prefixLength()) + "..."); - p->start("fping", {"-a", "-c", "5", "-g", targetAddressEntry.broadcast().toString() + "/" + QString::number(targetAddressEntry.prefixLength())}); - connect(p, SIGNAL(finished(int)), this, SLOT(broadcastPingFinished(int))); -} - -void DeviceMonitor::broadcastPingFinished(int exitCode) -{ - Q_UNUSED(exitCode); - log("Broadcast ping finished"); - QProcess *p = static_cast(sender()); - p->deleteLater(); -} - void DeviceMonitor::log(const QString &message) { qCDebug(dcNetworkDetector()).noquote().nospace() << m_name << " (" << m_macAddress << ", " << m_ipAddress << "): " << message; diff --git a/networkdetector/devicemonitor.h b/networkdetector/devicemonitor.h index 822900e4..a9bd9f33 100644 --- a/networkdetector/devicemonitor.h +++ b/networkdetector/devicemonitor.h @@ -3,6 +3,7 @@ #include #include +#include class DeviceMonitor : public QObject { @@ -25,7 +26,6 @@ private: void lookupArpCache(); void arping(); void ping(); - void broadcastPing(); void log(const QString &message); void warn(const QString &message); @@ -34,22 +34,19 @@ private slots: void arpLookupFinished(int exitCode); void arpingFinished(int exitCode); void pingFinished(int exitCode); - void broadcastPingFinished(int exitCode); private: QString m_name; QString m_macAddress; QString m_ipAddress; + QDateTime m_lastSeenTime; bool m_reachable = false; - int m_gracePeriod = 5; QProcess *m_arpLookupProcess = nullptr; QProcess *m_arpingProcess = nullptr; QProcess *m_pingProcess = nullptr; - int m_failedPings = 0; - }; #endif // DEVICEMONITOR_H diff --git a/networkdetector/devicepluginnetworkdetector.cpp b/networkdetector/devicepluginnetworkdetector.cpp index 7a56cf86..ae6bd65c 100644 --- a/networkdetector/devicepluginnetworkdetector.cpp +++ b/networkdetector/devicepluginnetworkdetector.cpp @@ -57,12 +57,13 @@ DevicePluginNetworkDetector::DevicePluginNetworkDetector() { m_discovery = new Discovery(this); connect(m_discovery, &Discovery::finished, this, &DevicePluginNetworkDetector::discoveryFinished); + + m_broadcastPing = new BroadcastPing(this); + connect(m_broadcastPing, &BroadcastPing::finished, this, &DevicePluginNetworkDetector::broadcastPingFinished); } DevicePluginNetworkDetector::~DevicePluginNetworkDetector() { - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); - if (m_discovery->isRunning()) { m_discovery->abort(); } @@ -70,8 +71,6 @@ DevicePluginNetworkDetector::~DevicePluginNetworkDetector() void DevicePluginNetworkDetector::init() { - m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); - connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginNetworkDetector::onPluginTimer); } DeviceManager::DeviceSetupStatus DevicePluginNetworkDetector::setupDevice(Device *device) @@ -86,7 +85,13 @@ DeviceManager::DeviceSetupStatus DevicePluginNetworkDetector::setupDevice(Device connect(monitor, &DeviceMonitor::addressChanged, this, &DevicePluginNetworkDetector::deviceAddressChanged); connect(monitor, &DeviceMonitor::seen, this, &DevicePluginNetworkDetector::deviceSeen); m_monitors.insert(monitor, device); - monitor->update(); + + if (!m_pluginTimer) { + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(30); + connect(m_pluginTimer, &PluginTimer::timeout, m_broadcastPing, &BroadcastPing::run); + + m_broadcastPing->run(); + } return DeviceManager::DeviceSetupStatusSuccess; } @@ -114,9 +119,15 @@ void DevicePluginNetworkDetector::deviceRemoved(Device *device) DeviceMonitor *monitor = m_monitors.key(device); m_monitors.remove(monitor); delete monitor; + + if (m_monitors.isEmpty()) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); + m_pluginTimer = nullptr; + + } } -void DevicePluginNetworkDetector::onPluginTimer() +void DevicePluginNetworkDetector::broadcastPingFinished() { foreach (DeviceMonitor *monitor, m_monitors.keys()) { monitor->update(); diff --git a/networkdetector/devicepluginnetworkdetector.h b/networkdetector/devicepluginnetworkdetector.h index 197d0a7c..e890aeb2 100644 --- a/networkdetector/devicepluginnetworkdetector.h +++ b/networkdetector/devicepluginnetworkdetector.h @@ -29,6 +29,7 @@ #include "discovery.h" #include "plugintimer.h" #include "devicemonitor.h" +#include "broadcastping.h" #include #include @@ -59,11 +60,12 @@ private slots: void deviceAddressChanged(const QString &address); void deviceSeen(); - void onPluginTimer(); + void broadcastPingFinished(); private: PluginTimer *m_pluginTimer = nullptr; Discovery *m_discovery = nullptr; + BroadcastPing *m_broadcastPing = nullptr; QHash m_monitors; }; diff --git a/networkdetector/networkdetector.pro b/networkdetector/networkdetector.pro index 7e1b989d..ccf6f674 100644 --- a/networkdetector/networkdetector.pro +++ b/networkdetector/networkdetector.pro @@ -8,12 +8,14 @@ SOURCES += \ devicepluginnetworkdetector.cpp \ host.cpp \ discovery.cpp \ - devicemonitor.cpp + devicemonitor.cpp \ + broadcastping.cpp HEADERS += \ devicepluginnetworkdetector.h \ host.h \ discovery.h \ - devicemonitor.h + devicemonitor.h \ + broadcastping.h