Bring back ICMP ping, introduce a grace period param

This commit is contained in:
Michael Zanetti 2019-02-11 18:18:32 +01:00
parent 5305013065
commit 29a8fae0da
4 changed files with 138 additions and 63 deletions

View File

@ -4,17 +4,20 @@
#include <QNetworkInterface> #include <QNetworkInterface>
DeviceMonitor::DeviceMonitor(const QString &macAddress, const QString &ipAddress, QObject *parent): DeviceMonitor::DeviceMonitor(const QString &name, const QString &macAddress, const QString &ipAddress, bool initialState, QObject *parent):
QObject(parent) QObject(parent),
m_name(name),
m_macAddress(macAddress),
m_ipAddress(ipAddress),
m_reachable(initialState)
{ {
m_host = new Host();
m_host->setMacAddress(macAddress);
m_host->setAddress(ipAddress);
m_host->setReachable(false);
m_arpLookupProcess = new QProcess(this); m_arpLookupProcess = new QProcess(this);
connect(m_arpLookupProcess, SIGNAL(finished(int)), this, SLOT(arpLookupFinished(int))); connect(m_arpLookupProcess, SIGNAL(finished(int)), this, SLOT(arpLookupFinished(int)));
m_arpingProcess = new QProcess(this);
m_arpingProcess->setReadChannelMode(QProcess::MergedChannels);
connect(m_arpingProcess, SIGNAL(finished(int)), this, SLOT(arpingFinished(int)));
m_pingProcess = new QProcess(this); m_pingProcess = new QProcess(this);
m_pingProcess->setReadChannelMode(QProcess::MergedChannels); m_pingProcess->setReadChannelMode(QProcess::MergedChannels);
connect(m_pingProcess, SIGNAL(finished(int)), this, SLOT(pingFinished(int))); connect(m_pingProcess, SIGNAL(finished(int)), this, SLOT(pingFinished(int)));
@ -22,13 +25,17 @@ DeviceMonitor::DeviceMonitor(const QString &macAddress, const QString &ipAddress
DeviceMonitor::~DeviceMonitor() DeviceMonitor::~DeviceMonitor()
{ {
delete m_host; }
void DeviceMonitor::setGracePeriod(int minutes)
{
m_gracePeriod = minutes;
} }
void DeviceMonitor::update() void DeviceMonitor::update()
{ {
if (m_pingProcess->state() != QProcess::NotRunning) { if (m_arpingProcess->state() != QProcess::NotRunning || m_pingProcess->state() != QProcess::NotRunning) {
qCDebug(dcNetworkDetector()) << "Previous ping still running for device" << m_host->address() << ". Not updating."; // log("Previous ping still running. Not updating.");
return; return;
} }
lookupArpCache(); lookupArpCache();
@ -39,57 +46,31 @@ void DeviceMonitor::lookupArpCache()
m_arpLookupProcess->start("ip", {"-4", "-s", "neighbor", "list"}); m_arpLookupProcess->start("ip", {"-4", "-s", "neighbor", "list"});
} }
void DeviceMonitor::ping()
{
qCDebug(dcNetworkDetector()) << "Sending ARP Ping to" << m_host->hostName() << m_host->macAddress() << m_host->address();
QNetworkInterface targetInterface;
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) {
QHostAddress clientAddress(m_host->address());
if (clientAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) {
targetInterface = interface;
break;
}
}
}
if (!targetInterface.isValid()) {
qCWarning(dcNetworkDetector()) << "Could not find a suitable interface to ping for" << m_host->address();
if (m_host->reachable()) {
m_host->setReachable(false);
emit reachableChanged(false);
}
return;
}
m_pingProcess->start("arping", {"-I", targetInterface.name(), "-f", "-w", "90", m_host->address()});
}
void DeviceMonitor::arpLookupFinished(int exitCode) void DeviceMonitor::arpLookupFinished(int exitCode)
{ {
if (exitCode != 0) { if (exitCode != 0) {
qCWarning(dcNetworkDetector()) << "Error looking up ARP cache."; warn("Error looking up ARP cache.");
return; return;
} }
QString data = QString::fromLatin1(m_arpLookupProcess->readAll()); QString data = QString::fromLatin1(m_arpLookupProcess->readAll());
bool found = false; bool found = false;
bool needsPing = true; bool needsPing = true;
QString mostRecentIP = m_host->address(); QString mostRecentIP = m_ipAddress;
qlonglong secsSinceLastSeen = -1; qlonglong secsSinceLastSeen = -1;
foreach (QString line, data.split('\n')) { foreach (QString line, data.split('\n')) {
line.replace(QRegExp("[ ]{1,}"), " "); line.replace(QRegExp("[ ]{1,}"), " ");
QStringList parts = line.split(" "); QStringList parts = line.split(" ");
int lladdrIndex = parts.indexOf("lladdr"); int lladdrIndex = parts.indexOf("lladdr");
if (lladdrIndex >= 0 && parts.count() > lladdrIndex + 1 && parts.at(lladdrIndex+1).toLower() == m_host->macAddress().toLower()) { if (lladdrIndex >= 0 && parts.count() > lladdrIndex + 1 && parts.at(lladdrIndex+1).toLower() == m_macAddress.toLower()) {
found = true; found = true;
QString entryIP = parts.first(); QString entryIP = parts.first();
if (parts.last() == "REACHABLE") { if (parts.last() == "REACHABLE") {
qCDebug(dcNetworkDetector()) << "Device" << m_host->macAddress() << "found in ARP cache and claims to be REACHABLE"; log("Device found in ARP cache and claims to be REACHABLE (Cache IP: " + entryIP + ")");
if (!m_host->reachable()) { if (!m_reachable) {
m_host->setReachable(true); m_reachable = true;
emit reachableChanged(true); emit reachableChanged(true);
} }
m_host->seen();
emit seen(); emit seen();
// Verify if IP address is still the same // Verify if IP address is still the same
if (entryIP != mostRecentIP) { if (entryIP != mostRecentIP) {
@ -101,7 +82,7 @@ void DeviceMonitor::arpLookupFinished(int exitCode)
break; break;
} else { } else {
// ARP claims the device to be stale... Flagging device to require a ping. // ARP claims the device to be stale... Flagging device to require a ping.
qCDebug(dcNetworkDetector()) << "Device" << m_host->macAddress() << "found in ARP cache with IP" << entryIP << "but is marked as" << parts.last(); log("Device found in ARP cache but is marked as " + parts.last() + " (Cache IP: " + entryIP + ")");
int usedIndex = parts.indexOf("used"); int usedIndex = parts.indexOf("used");
if (usedIndex >= 0 && parts.count() > usedIndex + 1) { if (usedIndex >= 0 && parts.count() > usedIndex + 1) {
@ -115,36 +96,88 @@ void DeviceMonitor::arpLookupFinished(int exitCode)
} }
} }
} }
if (mostRecentIP != m_host->address()) { if (mostRecentIP != m_ipAddress) {
qCDebug(dcNetworkDetector()) << "IP seems to have changed IP:" << m_host->address() << "->" << mostRecentIP; log("Device has changed IP: " + m_ipAddress + " -> " + mostRecentIP + ")");
m_host->setAddress(mostRecentIP); m_ipAddress = mostRecentIP;
emit addressChanged(mostRecentIP); emit addressChanged(mostRecentIP);
} }
if (!found) { if (!found) {
qCDebug(dcNetworkDetector()) << "Device" << m_host->macAddress() << "not found in ARP cache."; log("Device not found in ARP cache.");
ping(); arping();
} else if (needsPing) { } else if (needsPing) {
arping();
}
}
void DeviceMonitor::arping()
{
log("Sending ARP Ping...");
QNetworkInterface targetInterface;
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) {
QHostAddress clientAddress(m_ipAddress);
if (clientAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) {
targetInterface = interface;
break;
}
}
}
if (!targetInterface.isValid()) {
warn("Could not find a suitable interface to ARP Ping.");
if (m_reachable) {
m_reachable = false;
emit reachableChanged(false);
}
return;
}
m_arpingProcess->start("arping", {"-I", targetInterface.name(), "-f", "-w", "30", m_ipAddress});
}
void DeviceMonitor::arpingFinished(int exitCode)
{
if (exitCode == 0) {
// we were able to ping the device
log("ARP Ping successful.");
if (!m_reachable) {
m_reachable = true;
emit reachableChanged(true);
}
emit seen();
m_failedPings = 0;
} else {
log("ARP Ping failed.");
ping(); ping();
} }
// read data to discard it from socket
QString data = QString::fromLatin1(m_pingProcess->readAll());
Q_UNUSED(data)
// qCDebug(dcNetworkDetector()) << "have ping data" << data;
}
void DeviceMonitor::ping()
{
log("Sending ICMP Ping...");
m_pingProcess->start("ping", {"-c", "30", m_ipAddress});
} }
void DeviceMonitor::pingFinished(int exitCode) void DeviceMonitor::pingFinished(int exitCode)
{ {
if (exitCode == 0) { if (exitCode == 0) {
// we were able to ping the device // we were able to ping the device
qCDebug(dcNetworkDetector()) << "Ping successful for" << m_host->macAddress() << m_host->address(); log("ICMP Ping successful.");
m_host->seen(); if (!m_reachable) {
if (!m_host->reachable()) { m_reachable = true;
m_host->setReachable(true);
emit reachableChanged(true); emit reachableChanged(true);
} }
emit seen(); emit seen();
m_failedPings = 0; m_failedPings = 0;
} else { } else {
qCDebug(dcNetworkDetector()) << "Could not ping device" << m_host->macAddress() << m_host->address();
m_failedPings++; m_failedPings++;
if (m_failedPings > 3 && m_host->reachable()) { log("ICMP Ping failed for " + QString::number(m_failedPings) + " times. (Grace Period: " + QString::number(m_gracePeriod) + ")");
m_host->setReachable(false); if (m_failedPings > m_gracePeriod && m_reachable) {
log("Marking device as offline.");
m_reachable = false;
emit reachableChanged(false); emit reachableChanged(false);
} }
} }
@ -153,3 +186,13 @@ void DeviceMonitor::pingFinished(int exitCode)
Q_UNUSED(data) Q_UNUSED(data)
// qCDebug(dcNetworkDetector()) << "have ping data" << data; // qCDebug(dcNetworkDetector()) << "have ping data" << data;
} }
void DeviceMonitor::log(const QString &message)
{
qCDebug(dcNetworkDetector()).noquote().nospace() << m_name << " (" << m_macAddress << ", " << m_ipAddress << "): " << message;
}
void DeviceMonitor::warn(const QString &message)
{
qCWarning(dcNetworkDetector()).noquote().nospace() << m_name << " (" << m_macAddress << ", " << m_ipAddress << "): " << message;
}

View File

@ -4,16 +4,16 @@
#include <QObject> #include <QObject>
#include <QProcess> #include <QProcess>
#include "host.h"
class DeviceMonitor : public QObject class DeviceMonitor : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit DeviceMonitor(const QString &macAddress, const QString &ipAddress, QObject *parent = nullptr); explicit DeviceMonitor(const QString &name, const QString &macAddress, const QString &ipAddress, bool initialState, QObject *parent = nullptr);
~DeviceMonitor(); ~DeviceMonitor();
void setGracePeriod(int minutes);
void update(); void update();
signals: signals:
@ -23,16 +23,29 @@ signals:
private: private:
void lookupArpCache(); void lookupArpCache();
void arping();
void ping(); void ping();
void log(const QString &message);
void warn(const QString &message);
private slots: private slots:
void arpLookupFinished(int exitCode); void arpLookupFinished(int exitCode);
void arpingFinished(int exitCode);
void pingFinished(int exitCode); void pingFinished(int exitCode);
private: private:
Host *m_host; QString m_name;
QProcess *m_arpLookupProcess; QString m_macAddress;
QProcess *m_pingProcess; QString m_ipAddress;
bool m_reachable = false;
int m_gracePeriod = 5;
QProcess *m_arpLookupProcess = nullptr;
QProcess *m_arpingProcess = nullptr;
QProcess *m_pingProcess = nullptr;
int m_failedPings = 0; int m_failedPings = 0;
}; };

View File

@ -77,7 +77,11 @@ void DevicePluginNetworkDetector::init()
DeviceManager::DeviceSetupStatus DevicePluginNetworkDetector::setupDevice(Device *device) DeviceManager::DeviceSetupStatus DevicePluginNetworkDetector::setupDevice(Device *device)
{ {
qCDebug(dcNetworkDetector()) << "Setup" << device->name() << device->params(); qCDebug(dcNetworkDetector()) << "Setup" << device->name() << device->params();
DeviceMonitor *monitor = new DeviceMonitor(device->paramValue(networkDeviceDeviceMacAddressParamTypeId).toString(), device->paramValue(networkDeviceDeviceAddressParamTypeId).toString(), this); DeviceMonitor *monitor = new DeviceMonitor(device->name(),
device->paramValue(networkDeviceDeviceMacAddressParamTypeId).toString(),
device->paramValue(networkDeviceDeviceAddressParamTypeId).toString(),
device->stateValue(networkDeviceIsPresentStateTypeId).toBool(),
this);
connect(monitor, &DeviceMonitor::reachableChanged, this, &DevicePluginNetworkDetector::deviceReachableChanged); connect(monitor, &DeviceMonitor::reachableChanged, this, &DevicePluginNetworkDetector::deviceReachableChanged);
connect(monitor, &DeviceMonitor::addressChanged, this, &DevicePluginNetworkDetector::deviceAddressChanged); connect(monitor, &DeviceMonitor::addressChanged, this, &DevicePluginNetworkDetector::deviceAddressChanged);
connect(monitor, &DeviceMonitor::seen, this, &DevicePluginNetworkDetector::deviceSeen); connect(monitor, &DeviceMonitor::seen, this, &DevicePluginNetworkDetector::deviceSeen);
@ -124,7 +128,15 @@ void DevicePluginNetworkDetector::discoveryFinished(const QList<Host> &hosts)
qCDebug(dcNetworkDetector()) << "Discovery finished. Found" << hosts.count() << "devices"; qCDebug(dcNetworkDetector()) << "Discovery finished. Found" << hosts.count() << "devices";
QList<DeviceDescriptor> discoveredDevices; QList<DeviceDescriptor> discoveredDevices;
foreach (const Host &host, hosts) { foreach (const Host &host, hosts) {
DeviceDescriptor descriptor(networkDeviceDeviceClassId, (host.hostName().isEmpty() ? host.address() : host.hostName() + "(" + host.address() + ")"), host.macAddress());
DeviceDescriptor descriptor(networkDeviceDeviceClassId, host.hostName().isEmpty() ? host.address() : host.hostName(), host.address() + " (" + host.macAddress() + ")");
foreach (Device *existingDevice, myDevices()) {
if (existingDevice->paramValue(networkDeviceDeviceMacAddressParamTypeId).toString() == host.macAddress()) {
descriptor.setDeviceId(existingDevice->id());
break;
}
}
ParamList paramList; ParamList paramList;
Param macAddress(networkDeviceDeviceMacAddressParamTypeId, host.macAddress()); Param macAddress(networkDeviceDeviceMacAddressParamTypeId, host.macAddress());

View File

@ -34,6 +34,13 @@
"displayName": "hardware address", "displayName": "hardware address",
"type": "QString", "type": "QString",
"inputType": "TextLine" "inputType": "TextLine"
},
{
"id": "40116f86-e6b3-4a20-b1e9-e1bd4b6d5b70",
"name": "gracePeriod",
"displayName": "Grace period (Minutes)",
"type": "uint",
"defaultValue": 5
} }
], ],
"stateTypes": [ "stateTypes": [