Merge PR #511: Network device monitor
This commit is contained in:
commit
06ed86bcdd
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -41,6 +41,8 @@
|
||||
|
||||
NYMEA_LOGGING_CATEGORY(dcMacAddressDatabase, "MacAddressDatabase")
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
MacAddressDatabase::MacAddressDatabase(QObject *parent) : QObject(parent)
|
||||
{
|
||||
// Find database in system data locations
|
||||
@ -96,7 +98,7 @@ bool MacAddressDatabase::available() const
|
||||
|
||||
MacAddressDatabaseReply *MacAddressDatabase::lookupMacAddress(const QString &macAddress)
|
||||
{
|
||||
MacAddressDatabaseReply *reply = new MacAddressDatabaseReply(this);
|
||||
MacAddressDatabaseReplyImpl *reply = new MacAddressDatabaseReplyImpl(this);
|
||||
connect(reply, &MacAddressDatabaseReply::finished, reply, &MacAddressDatabaseReply::deleteLater);
|
||||
reply->m_macAddress = macAddress;
|
||||
|
||||
@ -140,6 +142,7 @@ bool MacAddressDatabase::initDatabase()
|
||||
return false;
|
||||
}
|
||||
|
||||
qCInfo(dcMacAddressDatabase()) << "Database initialized successfully" << m_databaseName;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -161,7 +164,7 @@ void MacAddressDatabase::onLookupFinished()
|
||||
{
|
||||
if (m_currentReply) {
|
||||
QString manufacturer = m_futureWatcher->future().result();
|
||||
qCDebug(dcMacAddressDatabase()) << "Manufacturer lookup for" << m_currentReply->macAddress() << "finished:" << manufacturer << QDateTime::currentMSecsSinceEpoch() - m_currentReply->m_startTimestamp << "ms";
|
||||
qCInfo(dcMacAddressDatabase()) << "Manufacturer lookup for" << m_currentReply->macAddress() << "finished:" << manufacturer << QDateTime::currentMSecsSinceEpoch() - m_currentReply->m_startTimestamp << "ms";
|
||||
m_currentReply->m_manufacturer = manufacturer;
|
||||
emit m_currentReply->finished();
|
||||
m_currentReply = nullptr;
|
||||
@ -172,7 +175,7 @@ void MacAddressDatabase::onLookupFinished()
|
||||
|
||||
QString MacAddressDatabase::lookupMacAddressVendorInternal(const QString &macAddress)
|
||||
{
|
||||
qCDebug(dcMacAddressDatabase()) << "Start looking up vendor for" << macAddress;
|
||||
qCInfo(dcMacAddressDatabase()) << "Start looking up vendor for" << macAddress;
|
||||
// Convert the mac address string to upper like in the database and remove : since they have been removed for size reasons
|
||||
QString fullMacAddressString = QString(macAddress).toUpper().remove(":");
|
||||
|
||||
@ -221,3 +224,4 @@ QString MacAddressDatabase::lookupMacAddressVendorInternal(const QString &macAdd
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -36,30 +36,11 @@
|
||||
#include <QSqlDatabase>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include "libnymea.h"
|
||||
#include "macaddressdatabasereplyimpl.h"
|
||||
|
||||
class LIBNYMEA_EXPORT MacAddressDatabaseReply : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class MacAddressDatabase;
|
||||
namespace nymeaserver {
|
||||
|
||||
public:
|
||||
QString macAddress() const { return m_macAddress; };
|
||||
QString manufacturer() const { return m_manufacturer; };
|
||||
|
||||
private:
|
||||
explicit MacAddressDatabaseReply(QObject *parent = nullptr) : QObject(parent) { };
|
||||
QString m_macAddress;
|
||||
QString m_manufacturer;
|
||||
qint64 m_startTimestamp;
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
};
|
||||
|
||||
|
||||
class LIBNYMEA_EXPORT MacAddressDatabase : public QObject
|
||||
class MacAddressDatabase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -77,9 +58,9 @@ private:
|
||||
QString m_connectionName;
|
||||
QString m_databaseName = "/usr/share/nymea/mac-addresses.db";
|
||||
|
||||
MacAddressDatabaseReply *m_currentReply = nullptr;
|
||||
MacAddressDatabaseReplyImpl *m_currentReply = nullptr;
|
||||
QFutureWatcher<QString> *m_futureWatcher = nullptr;
|
||||
QQueue<MacAddressDatabaseReply *> m_pendingReplies;
|
||||
QQueue<MacAddressDatabaseReplyImpl *> m_pendingReplies;
|
||||
|
||||
bool initDatabase();
|
||||
void runNextLookup();
|
||||
@ -90,4 +71,6 @@ private slots:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MACADDRESSDATABASE_H
|
||||
@ -0,0 +1,51 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "macaddressdatabasereplyimpl.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
MacAddressDatabaseReplyImpl::MacAddressDatabaseReplyImpl(QObject *parent) :
|
||||
MacAddressDatabaseReply(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString MacAddressDatabaseReplyImpl::macAddress() const
|
||||
{
|
||||
return m_macAddress;
|
||||
}
|
||||
|
||||
QString MacAddressDatabaseReplyImpl::manufacturer() const
|
||||
{
|
||||
return m_manufacturer;
|
||||
}
|
||||
|
||||
}
|
||||
62
libnymea-core/hardware/network/macaddressdatabasereplyimpl.h
Normal file
62
libnymea-core/hardware/network/macaddressdatabasereplyimpl.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef MACADDRESSDATABASEREPLYIMPL_H
|
||||
#define MACADDRESSDATABASEREPLYIMPL_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <network/macaddressdatabasereply.h>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class MacAddressDatabaseReplyImpl : public MacAddressDatabaseReply
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class MacAddressDatabase;
|
||||
|
||||
public:
|
||||
explicit MacAddressDatabaseReplyImpl(QObject *parent = nullptr);
|
||||
~MacAddressDatabaseReplyImpl() override = default;
|
||||
|
||||
QString macAddress() const override;
|
||||
QString manufacturer() const override;
|
||||
|
||||
private:
|
||||
QString m_macAddress;
|
||||
QString m_manufacturer;
|
||||
qint64 m_startTimestamp;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MACADDRESSDATABASEREPLYIMPL_H
|
||||
610
libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp
Normal file
610
libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp
Normal file
@ -0,0 +1,610 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdevicediscoveryimpl.h"
|
||||
|
||||
#include "nymeasettings.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
#include <network/ping.h>
|
||||
#include <network/arpsocket.h>
|
||||
#include <network/networkutils.h>
|
||||
|
||||
NYMEA_LOGGING_CATEGORY(dcNetworkDeviceDiscovery, "NetworkDeviceDiscovery")
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
NetworkDeviceDiscoveryImpl::NetworkDeviceDiscoveryImpl(QObject *parent) :
|
||||
NetworkDeviceDiscovery(parent)
|
||||
{
|
||||
// Create ARP socket
|
||||
m_arpSocket = new ArpSocket(this);
|
||||
connect(m_arpSocket, &ArpSocket::arpResponse, this, &NetworkDeviceDiscoveryImpl::onArpResponseReceived);
|
||||
bool arpAvailable = m_arpSocket->openSocket();
|
||||
if (!arpAvailable)
|
||||
m_arpSocket->closeSocket();
|
||||
|
||||
// Create ping socket
|
||||
m_ping = new Ping(this);
|
||||
if (!m_ping->available())
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Failed to create ping tool" << m_ping->error();
|
||||
|
||||
// Init MAC database if available
|
||||
m_macAddressDatabase = new MacAddressDatabase(this);
|
||||
|
||||
// Timer for max duration af a discovery
|
||||
m_discoveryTimer = new QTimer(this);
|
||||
m_discoveryTimer->setInterval(20000);
|
||||
m_discoveryTimer->setSingleShot(true);
|
||||
connect(m_discoveryTimer, &QTimer::timeout, this, [=](){
|
||||
if (m_runningPingRepies.isEmpty() && m_currentReply) {
|
||||
finishDiscovery();
|
||||
}
|
||||
});
|
||||
|
||||
// Timer for updating the monitors
|
||||
m_monitorTimer = new QTimer(this);
|
||||
m_monitorTimer->setInterval(5000);
|
||||
m_monitorTimer->setSingleShot(false);
|
||||
connect(m_monitorTimer, &QTimer::timeout, this, &NetworkDeviceDiscoveryImpl::evaluateMonitors);
|
||||
|
||||
if (!arpAvailable && !m_ping->available()) {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Network device discovery is not available on this system.";
|
||||
} else {
|
||||
qCInfo(dcNetworkDeviceDiscovery()) << "Created successfully";
|
||||
}
|
||||
|
||||
m_cacheSettings = new QSettings(NymeaSettings::cachePath() + "/network-device-discovery.cache", QSettings::IniFormat);
|
||||
loadNetworkDeviceCache();
|
||||
|
||||
// Start the timer only if the resource is available
|
||||
if (available()) {
|
||||
// Start the monitor timer in any case, we do also the cache cleanup there...
|
||||
m_monitorTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkDeviceDiscoveryImpl::~NetworkDeviceDiscoveryImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
|
||||
{
|
||||
if (m_currentReply) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Discovery already running. Returning current pending discovery reply...";
|
||||
return m_currentReply;
|
||||
}
|
||||
|
||||
m_currentReply = new NetworkDeviceDiscoveryReplyImpl(this);
|
||||
|
||||
if (!available()) {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "The network discovery is not available. Please make sure the binary has the required capability (CAP_NET_RAW) or start the application as root.";
|
||||
// Finish the discovery in the next event loop so anny connections after the creation will work as expected
|
||||
QTimer::singleShot(0, this, &NetworkDeviceDiscoveryImpl::finishDiscovery);
|
||||
return m_currentReply;
|
||||
}
|
||||
|
||||
connect(m_currentReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, this, &NetworkDeviceDiscoveryImpl::updateCache);
|
||||
qCInfo(dcNetworkDeviceDiscovery()) << "Starting network device discovery ...";
|
||||
|
||||
if (m_ping->available())
|
||||
pingAllNetworkDevices();
|
||||
|
||||
if (m_arpSocket->isOpen())
|
||||
m_arpSocket->sendRequest();
|
||||
|
||||
m_discoveryTimer->start();
|
||||
|
||||
m_running = true;
|
||||
emit runningChanged(m_running);
|
||||
|
||||
return m_currentReply;
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscoveryImpl::available() const
|
||||
{
|
||||
return m_arpSocket->isOpen() || m_ping->available();
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscoveryImpl::enabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscoveryImpl::running() const
|
||||
{
|
||||
return m_running;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// Fill in cached information
|
||||
NetworkDeviceInfo info;
|
||||
if (m_networkInfoCache.contains(macAddress)) {
|
||||
info = m_networkInfoCache.value(macAddress);
|
||||
} else {
|
||||
info.setMacAddress(macAddress.toString());
|
||||
}
|
||||
|
||||
NetworkDeviceMonitorImpl *monitor = new NetworkDeviceMonitorImpl(macAddress, this);
|
||||
monitor->setNetworkDeviceInfo(info);
|
||||
m_monitors.insert(macAddress, monitor);
|
||||
|
||||
if (!available()) {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Registered monitor but the hardware resource is not available. The monitor will not work as expected" << monitor;
|
||||
return monitor;
|
||||
}
|
||||
|
||||
// Restart the monitor timer since we evaluate this one now
|
||||
m_monitorTimer->start();
|
||||
|
||||
if (!monitor->networkDeviceInfo().isValid()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Adding network device monitor for unresolved mac address. Starting a discovery...";
|
||||
discover();
|
||||
}
|
||||
|
||||
evaluateMonitor(monitor);
|
||||
|
||||
return monitor;
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::unregisterMonitor(const MacAddress &macAddress)
|
||||
{
|
||||
if (m_monitors.contains(macAddress)) {
|
||||
NetworkDeviceMonitor *monitor = m_monitors.take(macAddress);
|
||||
qCInfo(dcNetworkDeviceDiscovery()) << "Unregister" << monitor;
|
||||
monitor->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor)
|
||||
{
|
||||
unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress()));
|
||||
}
|
||||
|
||||
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
MacAddressDatabaseReply *NetworkDeviceDiscoveryImpl::lookupMacAddress(const QString &macAddress)
|
||||
{
|
||||
return m_macAddressDatabase->lookupMacAddress(macAddress);
|
||||
}
|
||||
|
||||
MacAddressDatabaseReply *NetworkDeviceDiscoveryImpl::lookupMacAddress(const MacAddress &macAddress)
|
||||
{
|
||||
return lookupMacAddress(macAddress.toString());
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscoveryImpl::sendArpRequest(const QHostAddress &address)
|
||||
{
|
||||
if (m_arpSocket && m_arpSocket->isOpen())
|
||||
return m_arpSocket->sendRequest(address);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
emit enabledChanged(m_enabled);
|
||||
// TODO: disable network discovery if false, not used for now
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::pingAllNetworkDevices()
|
||||
{
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Starting ping for all network devices...";
|
||||
foreach (const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
|
||||
if (networkInterface.flags().testFlag(QNetworkInterface::IsLoopBack))
|
||||
continue;
|
||||
|
||||
if (!networkInterface.flags().testFlag(QNetworkInterface::IsUp))
|
||||
continue;
|
||||
|
||||
if (!networkInterface.flags().testFlag(QNetworkInterface::IsRunning))
|
||||
continue;
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Verifying network interface" << networkInterface.name() << networkInterface.hardwareAddress() << "...";
|
||||
foreach (const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Checking entry" << entry.ip().toString();
|
||||
|
||||
// Only IPv4
|
||||
if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol)
|
||||
continue;
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Host address:" << entry.ip().toString();
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Broadcast address:" << entry.broadcast().toString();
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Netmask:" << entry.netmask().toString();
|
||||
quint32 addressRangeStart = entry.ip().toIPv4Address() & entry.netmask().toIPv4Address();
|
||||
quint32 addressRangeStop = entry.broadcast().toIPv4Address() | addressRangeStart;
|
||||
quint32 range = addressRangeStop - addressRangeStart;
|
||||
|
||||
// Let's scan only 255.255.255.0 networks for now
|
||||
if (range > 255)
|
||||
continue;
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Address range" << range << " | from" << QHostAddress(addressRangeStart).toString() << "-->" << QHostAddress(addressRangeStop).toString();
|
||||
// Send ping request to each address within the range
|
||||
for (quint32 i = 1; i < range; i++) {
|
||||
quint32 address = addressRangeStart + i;
|
||||
QHostAddress targetAddress(address);
|
||||
|
||||
// Skip our self
|
||||
if (targetAddress == entry.ip())
|
||||
continue;
|
||||
|
||||
PingReply *reply = ping(targetAddress);
|
||||
m_runningPingRepies.append(reply);
|
||||
connect(reply, &PingReply::finished, this, [=](){
|
||||
m_runningPingRepies.removeAll(reply);
|
||||
if (reply->error() == PingReply::ErrorNoError) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Ping response from" << targetAddress.toString() << reply->hostName() << reply->duration() << "ms";
|
||||
if (m_currentReply) {
|
||||
m_currentReply->processPingResponse(targetAddress, reply->hostName());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_runningPingRepies.isEmpty() && m_currentReply && !m_discoveryTimer->isActive()) {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "All ping replies finished for discovery." << m_currentReply->networkDeviceInfos().count();
|
||||
finishDiscovery();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, NetworkDeviceMonitorImpl *monitor)
|
||||
{
|
||||
// Save the last time we tried to communicate
|
||||
if (reply->error() == PingReply::ErrorNoError) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Ping response from" << monitor << reply->duration() << "ms";
|
||||
monitor->setLastSeen(QDateTime::currentDateTime());
|
||||
monitor->setReachable(true);
|
||||
} else {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << reply->error();
|
||||
monitor->setReachable(false);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
|
||||
{
|
||||
qCInfo(dcNetworkDeviceDiscovery()) << "Loading cached network device information from" << m_cacheSettings->fileName();
|
||||
|
||||
m_networkInfoCache.clear();
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
||||
foreach (const QString &macAddress, m_cacheSettings->childGroups()) {
|
||||
m_cacheSettings->beginGroup(macAddress);
|
||||
|
||||
MacAddress mac(macAddress);
|
||||
QDateTime lastSeen = QDateTime::fromMSecsSinceEpoch(m_cacheSettings->value("lastSeen").toLongLong());
|
||||
|
||||
// Remove the info from the cache if not seen fo the last 30 days...
|
||||
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("");
|
||||
continue;
|
||||
}
|
||||
|
||||
NetworkDeviceInfo info(mac.toString());
|
||||
info.setAddress(QHostAddress(m_cacheSettings->value("address").toString()));
|
||||
info.setHostName(m_cacheSettings->value("hostName").toString());
|
||||
info.setMacAddressManufacturer(m_cacheSettings->value("manufacturer").toString());
|
||||
info.setNetworkInterface(QNetworkInterface::interfaceFromName(m_cacheSettings->value("interface").toString()));
|
||||
|
||||
if (info.isValid() && info.isComplete()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Loaded cached" << info << "last seen" << lastSeen.toString();
|
||||
m_networkInfoCache[mac] = info;
|
||||
m_lastSeen[mac] = lastSeen;
|
||||
} else {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Clean up invalid cached network device info from cache" << info;
|
||||
m_cacheSettings->remove("");
|
||||
}
|
||||
|
||||
m_cacheSettings->endGroup(); // mac address
|
||||
}
|
||||
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
||||
|
||||
// We just did some housekeeping while loading from the cache
|
||||
m_lastCacheHousekeeping = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const MacAddress &macAddress)
|
||||
{
|
||||
if (macAddress.isNull())
|
||||
return;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo)
|
||||
{
|
||||
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
||||
m_cacheSettings->beginGroup(deviceInfo.macAddress());
|
||||
m_cacheSettings->setValue("address", deviceInfo.address().toString());
|
||||
m_cacheSettings->setValue("hostName", deviceInfo.hostName());
|
||||
m_cacheSettings->setValue("manufacturer", deviceInfo.macAddressManufacturer());
|
||||
m_cacheSettings->setValue("interface", deviceInfo.networkInterface().name());
|
||||
m_cacheSettings->setValue("lastSeen", convertMinuteBased(m_lastSeen.value(MacAddress(deviceInfo.macAddress()))).toMSecsSinceEpoch());
|
||||
m_cacheSettings->endGroup(); // mac address
|
||||
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo)
|
||||
{
|
||||
MacAddress macAddress(deviceInfo.macAddress());
|
||||
if (macAddress.isNull())
|
||||
return;
|
||||
|
||||
if (m_monitors.contains(macAddress)) {
|
||||
NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
|
||||
monitor->setNetworkDeviceInfo(deviceInfo);
|
||||
}
|
||||
|
||||
if (m_networkInfoCache.value(macAddress) == deviceInfo)
|
||||
return;
|
||||
|
||||
m_networkInfoCache[macAddress] = deviceInfo;
|
||||
saveNetworkDeviceCache(deviceInfo);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monitor)
|
||||
{
|
||||
// Start action if we have not seen the device for gracePeriod seconds
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
|
||||
bool requiresRefresh = false;
|
||||
if (monitor->lastSeen().isNull()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "requires refresh. Not seen since application start.";
|
||||
requiresRefresh = true;
|
||||
}
|
||||
|
||||
if (!requiresRefresh && currentDateTime > monitor->lastSeen().addSecs(m_monitorInterval)) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "requires refresh. Not see since" << (currentDateTime.toMSecsSinceEpoch() - monitor->lastSeen().toMSecsSinceEpoch()) / 1000.0 << "s";
|
||||
requiresRefresh = true;
|
||||
}
|
||||
|
||||
if (!requiresRefresh)
|
||||
return;
|
||||
|
||||
if (monitor->networkDeviceInfo().address().isNull())
|
||||
return;
|
||||
|
||||
// Try to ping first
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString();
|
||||
monitor->setLastConnectionAttempt(currentDateTime);
|
||||
|
||||
PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address());
|
||||
connect(reply, &PingReply::finished, monitor, [=](){
|
||||
processMonitorPingResult(reply, monitor);
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
m_lastSeen[macAddress] = now;
|
||||
|
||||
if (m_networkInfoCache.contains(macAddress)) {
|
||||
if (m_networkInfoCache.value(macAddress).address() != address) {
|
||||
m_networkInfoCache[macAddress].setAddress(address);
|
||||
saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
|
||||
}
|
||||
}
|
||||
|
||||
// Update the monitors
|
||||
NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
|
||||
if (monitor) {
|
||||
monitor->setLastSeen(now);
|
||||
monitor->setReachable(true);
|
||||
if (monitor->networkDeviceInfo().address() != address) {
|
||||
NetworkDeviceInfo info = monitor->networkDeviceInfo();
|
||||
info.setAddress(address);
|
||||
monitor->setNetworkDeviceInfo(info);
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "NetworkDeviceMonitor" << monitor << "ip address changed";
|
||||
emit monitor->networkDeviceInfoChanged(monitor->networkDeviceInfo());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have a reply running
|
||||
if (m_currentReply) {
|
||||
|
||||
// First process the response
|
||||
m_currentReply->processArpResponse(interface, address, macAddress);
|
||||
|
||||
// Check if we know the mac address manufacturer from the cache
|
||||
if (m_networkInfoCache.contains(macAddress)) {
|
||||
QString cachedManufacturer = m_networkInfoCache[macAddress].macAddressManufacturer();
|
||||
if (!cachedManufacturer.isEmpty()) {
|
||||
// Found the mac address manufacturer in the cache, let's use that
|
||||
m_currentReply->processMacManufacturer(macAddress, cachedManufacturer);
|
||||
}
|
||||
} else {
|
||||
// Lookup the mac address vendor if possible
|
||||
if (m_macAddressDatabase->available()) {
|
||||
MacAddressDatabaseReply *reply = m_macAddressDatabase->lookupMacAddress(macAddress.toString());
|
||||
connect(reply, &MacAddressDatabaseReply::finished, m_currentReply, [=](){
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "MAC manufacturer lookup finished for" << macAddress << ":" << reply->manufacturer();
|
||||
m_currentReply->processMacManufacturer(macAddress, reply->manufacturer());
|
||||
});
|
||||
} else {
|
||||
// Note: set the mac manufacturer explicitly to make the info complete
|
||||
m_currentReply->processMacManufacturer(macAddress, QString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscoveryImpl::longerAgoThan(const QDateTime &dateTime, uint seconds)
|
||||
{
|
||||
uint duration = (QDateTime::currentDateTime().toMSecsSinceEpoch() - dateTime.toMSecsSinceEpoch()) / 1000.0;
|
||||
return duration >= seconds;
|
||||
}
|
||||
|
||||
QDateTime NetworkDeviceDiscoveryImpl::convertMinuteBased(const QDateTime &dateTime)
|
||||
{
|
||||
// We store the date time on minute accuracy to have
|
||||
// less write cycles and a higher resolution is not necessary
|
||||
|
||||
// If the given datetime is null, use the current datetime
|
||||
QDateTime dateTimeToConvert;
|
||||
if (dateTime.isNull()) {
|
||||
dateTimeToConvert = QDateTime::currentDateTime();
|
||||
} else {
|
||||
dateTimeToConvert = dateTime;
|
||||
}
|
||||
|
||||
dateTimeToConvert.setTime(QTime(dateTimeToConvert.time().hour(), dateTimeToConvert.time().minute(), 0, 0));
|
||||
return dateTimeToConvert;
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::onArpResponseReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||
{
|
||||
// Ignore ARP from zero mac
|
||||
if (macAddress.isNull()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Ignoring ARP reply from" << address.toString() << macAddress.toString() << interface.name();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "ARP reply received" << address.toString() << macAddress.toString() << interface.name();
|
||||
processArpTraffic(interface, address, macAddress);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::onArpRequstReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||
{
|
||||
// Ignore ARP from zero mac
|
||||
if (macAddress.isNull()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Ignoring ARP reply from" << address.toString() << macAddress.toString() << interface.name();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "ARP request received" << address.toString() << macAddress.toString() << interface.name();
|
||||
processArpTraffic(interface, address, macAddress);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::evaluateMonitors()
|
||||
{
|
||||
bool monitorRequiresRediscovery = false;
|
||||
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors) {
|
||||
evaluateMonitor(monitor);
|
||||
|
||||
// Check if there is any monitor which has not be seen since
|
||||
if (!monitor->reachable() && monitor->lastConnectionAttempt().isValid() && longerAgoThan(monitor->lastSeen(), m_monitorInterval)) {
|
||||
monitorRequiresRediscovery = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (monitorRequiresRediscovery && longerAgoThan(m_lastDiscovery, m_rediscoveryInterval)) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "There are unreachable monitors and the last discovery is more than" << m_rediscoveryInterval << "s ago. Starting network discovery to search the monitored network devices...";
|
||||
discover();
|
||||
}
|
||||
|
||||
// Do some cache housekeeping if required
|
||||
if (m_lastCacheHousekeeping.addDays(1) < QDateTime::currentDateTime()) {
|
||||
qCInfo(dcNetworkDeviceDiscovery()) << "Starting cache housekeeping since it is more than one day since the last clanup...";
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
foreach (const MacAddress &mac, m_lastSeen.keys()) {
|
||||
// Remove the info from the cache if not seen fo the last 30 days...
|
||||
if (m_lastSeen.value(mac).date().addDays(m_cacheCleanupPeriod) < QDateTime::currentDateTime().date()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << mac.toString();
|
||||
removeFromNetworkDeviceCache(mac);
|
||||
}
|
||||
}
|
||||
|
||||
m_lastCacheHousekeeping = now;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryImpl::finishDiscovery()
|
||||
{
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Finishing discovery...";
|
||||
m_discoveryTimer->stop();
|
||||
|
||||
m_running = false;
|
||||
emit runningChanged(m_running);
|
||||
|
||||
emit networkDeviceInfoCacheUpdated();
|
||||
|
||||
m_lastDiscovery = QDateTime::currentDateTime();
|
||||
|
||||
m_currentReply->processDiscoveryFinished();
|
||||
m_currentReply->deleteLater();
|
||||
m_currentReply = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
137
libnymea-core/hardware/network/networkdevicediscoveryimpl.h
Normal file
137
libnymea-core/hardware/network/networkdevicediscoveryimpl.h
Normal file
@ -0,0 +1,137 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef NETWORKDEVICEDISCOVERYIMPL_H
|
||||
#define NETWORKDEVICEDISCOVERYIMPL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
#include <QLoggingCategory>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <network/networkdeviceinfo.h>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "macaddressdatabase.h"
|
||||
|
||||
#include "networkdevicemonitorimpl.h"
|
||||
#include "macaddressdatabasereplyimpl.h"
|
||||
#include "networkdevicediscoveryreplyimpl.h"
|
||||
|
||||
class Ping;
|
||||
class ArpSocket;
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery)
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class NetworkDeviceDiscoveryImpl : public NetworkDeviceDiscovery
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NetworkDeviceDiscoveryImpl(QObject *parent = nullptr);
|
||||
~NetworkDeviceDiscoveryImpl() override;
|
||||
|
||||
bool available() const override;
|
||||
bool enabled() const override;
|
||||
|
||||
bool running() const override;
|
||||
|
||||
NetworkDeviceDiscoveryReply *discover() override;
|
||||
|
||||
NetworkDeviceMonitor *registerMonitor(const MacAddress &macAddress) override;
|
||||
|
||||
void unregisterMonitor(const MacAddress &macAddress) override;
|
||||
void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override;
|
||||
|
||||
PingReply *ping(const QHostAddress &address) override;
|
||||
|
||||
MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override;
|
||||
MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) override;
|
||||
|
||||
bool sendArpRequest(const QHostAddress &address) override;
|
||||
|
||||
protected:
|
||||
void setEnabled(bool enabled) override;
|
||||
|
||||
private:
|
||||
MacAddressDatabase *m_macAddressDatabase = nullptr;
|
||||
ArpSocket *m_arpSocket = nullptr;
|
||||
Ping *m_ping = nullptr;
|
||||
bool m_enabled = true;
|
||||
bool m_running = false;
|
||||
|
||||
QTimer *m_discoveryTimer = nullptr;
|
||||
QTimer *m_monitorTimer = nullptr;
|
||||
|
||||
QDateTime m_lastDiscovery;
|
||||
QDateTime m_lastCacheHousekeeping;
|
||||
|
||||
uint m_rediscoveryInterval = 300; // 5 min
|
||||
uint m_monitorInterval = 60; // 1 min
|
||||
uint m_cacheCleanupPeriod = 30; // days
|
||||
|
||||
NetworkDeviceDiscoveryReplyImpl *m_currentReply = nullptr;
|
||||
QList<PingReply *> m_runningPingRepies;
|
||||
|
||||
QHash<MacAddress, NetworkDeviceMonitorImpl *> m_monitors;
|
||||
QHash<MacAddress, QDateTime> m_lastSeen;
|
||||
|
||||
QSettings *m_cacheSettings;
|
||||
QHash<MacAddress, NetworkDeviceInfo> m_networkInfoCache;
|
||||
|
||||
void pingAllNetworkDevices();
|
||||
|
||||
void processMonitorPingResult(PingReply *reply, NetworkDeviceMonitorImpl *monitor);
|
||||
|
||||
void loadNetworkDeviceCache();
|
||||
void removeFromNetworkDeviceCache(const MacAddress &macAddress);
|
||||
void saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo);
|
||||
|
||||
void updateCache(const NetworkDeviceInfo &deviceInfo);
|
||||
void evaluateMonitor(NetworkDeviceMonitorImpl *monitor);
|
||||
|
||||
void processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||
|
||||
// Time helpers
|
||||
bool longerAgoThan(const QDateTime &dateTime, uint minutes);
|
||||
QDateTime convertMinuteBased(const QDateTime &dateTime = QDateTime());
|
||||
|
||||
private slots:
|
||||
void onArpResponseReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||
void onArpRequstReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||
void evaluateMonitors();
|
||||
void finishDiscovery();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NETWORKDEVICEDISCOVERYIMPL_H
|
||||
@ -0,0 +1,221 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdevicediscoveryreplyimpl.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery)
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
NetworkDeviceDiscoveryReplyImpl::NetworkDeviceDiscoveryReplyImpl(QObject *parent) :
|
||||
NetworkDeviceDiscoveryReply(parent)
|
||||
{
|
||||
m_startTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
NetworkDeviceInfos NetworkDeviceDiscoveryReplyImpl::networkDeviceInfos() const
|
||||
{
|
||||
return m_networkDeviceInfos;
|
||||
}
|
||||
|
||||
NetworkDeviceInfos NetworkDeviceDiscoveryReplyImpl::virtualNetworkDeviceInfos() const
|
||||
{
|
||||
return m_virtualNetworkDeviceInfos;
|
||||
}
|
||||
|
||||
QString NetworkDeviceDiscoveryReplyImpl::macAddressFromHostAddress(const QHostAddress &address)
|
||||
{
|
||||
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
|
||||
if (info.address() == address) {
|
||||
return info.macAddress();
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscoveryReplyImpl::hasHostAddress(const QHostAddress &address)
|
||||
{
|
||||
return ! macAddressFromHostAddress(address).isEmpty();
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryReplyImpl::verifyComplete(const MacAddress &macAddress)
|
||||
{
|
||||
if (!m_networkDeviceCache.contains(macAddress))
|
||||
return;
|
||||
|
||||
if (m_networkDeviceCache[macAddress].isComplete() && m_networkDeviceCache[macAddress].isValid()) {
|
||||
if (m_networkDeviceInfos.hasMacAddress(macAddress)) {
|
||||
if (m_networkDeviceInfos.get(macAddress) != m_networkDeviceCache.value(macAddress)) {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Already complete network device info changed during discovery process! Please report a bug if you see this message.";
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << m_networkDeviceInfos.get(macAddress);
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << m_networkDeviceCache.value(macAddress);
|
||||
}
|
||||
} else {
|
||||
m_networkDeviceInfos.append(m_networkDeviceCache.value(macAddress));
|
||||
emit networkDeviceInfoAdded(m_networkDeviceCache[macAddress]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryReplyImpl::processPingResponse(const QHostAddress &address, const QString &hostName)
|
||||
{
|
||||
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
|
||||
if (info.address() == address) {
|
||||
// Already found info, set host name and check if complete
|
||||
MacAddress macAddress(info.macAddress());
|
||||
m_networkDeviceCache[macAddress].setHostName(hostName);
|
||||
verifyComplete(macAddress);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown and we have no mac address yet, add it to the ping cache
|
||||
NetworkDeviceInfo info;
|
||||
info.setAddress(address);
|
||||
info.setHostName(hostName);
|
||||
m_pingCache.insert(address, info);
|
||||
emit hostAddressDiscovered(address);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryReplyImpl::processArpResponse(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||
{
|
||||
if (m_pingCache.contains(address)) {
|
||||
// We know this device from a ping response
|
||||
NetworkDeviceInfo info = m_pingCache.take(address);
|
||||
info.setAddress(address);
|
||||
info.setNetworkInterface(interface);
|
||||
info.setMacAddress(macAddress.toString());
|
||||
m_networkDeviceCache[macAddress] = info;
|
||||
emit hostAddressDiscovered(address);
|
||||
} else {
|
||||
if (m_networkDeviceCache.contains(macAddress)) {
|
||||
m_networkDeviceCache[macAddress].setAddress(address);
|
||||
m_networkDeviceCache[macAddress].setNetworkInterface(interface);
|
||||
} else {
|
||||
NetworkDeviceInfo info(macAddress.toString());
|
||||
info.setAddress(address);
|
||||
info.setNetworkInterface(interface);
|
||||
m_networkDeviceCache[macAddress] = info;
|
||||
}
|
||||
}
|
||||
|
||||
verifyComplete(macAddress);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryReplyImpl::processMacManufacturer(const MacAddress &macAddress, const QString &manufacturer)
|
||||
{
|
||||
if (macAddress.isNull())
|
||||
return;
|
||||
|
||||
if (m_networkDeviceCache.contains(macAddress)) {
|
||||
m_networkDeviceCache[macAddress].setMacAddressManufacturer(manufacturer);
|
||||
} else {
|
||||
NetworkDeviceInfo info(macAddress.toString());
|
||||
info.setMacAddressManufacturer(manufacturer);
|
||||
m_networkDeviceCache[macAddress] = info;
|
||||
}
|
||||
|
||||
verifyComplete(macAddress);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscoveryReplyImpl::processDiscoveryFinished()
|
||||
{
|
||||
// Lets see if we have any incomplete infos but enougth data to be shown
|
||||
foreach (const MacAddress &macAddress, m_networkDeviceCache.keys()) {
|
||||
// If already in the result, ignore it
|
||||
if (m_networkDeviceInfos.hasMacAddress(macAddress))
|
||||
continue;
|
||||
|
||||
NetworkDeviceInfo info = m_networkDeviceCache.value(macAddress);
|
||||
MacAddress infoMacAddress(info.macAddress());
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info
|
||||
<< "Valid:" << info.isValid()
|
||||
<< "Complete:" << info.isComplete()
|
||||
<< info.incompleteProperties();
|
||||
|
||||
// We need at least a valid mac address and a valid ip address, the rest ist pure informative
|
||||
if (infoMacAddress == macAddress && !infoMacAddress.isNull() && !info.address().isNull()) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Adding incomplete" << info << "to the final result:" << info.incompleteProperties();
|
||||
// Note: makeing it complete
|
||||
m_networkDeviceCache[macAddress].setAddress(info.address());
|
||||
m_networkDeviceCache[macAddress].setHostName(info.hostName());
|
||||
m_networkDeviceCache[macAddress].setMacAddress(info.macAddress());
|
||||
m_networkDeviceCache[macAddress].setMacAddressManufacturer(info.macAddressManufacturer());
|
||||
m_networkDeviceCache[macAddress].setNetworkInterface(info.networkInterface());
|
||||
verifyComplete(macAddress);
|
||||
}
|
||||
}
|
||||
|
||||
// Done, lets sort the result and inform
|
||||
m_networkDeviceInfos.sortNetworkDevices();
|
||||
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startTimestamp;
|
||||
qCInfo(dcNetworkDeviceDiscovery()) << "Discovery finished. Found" << networkDeviceInfos().count() << "network devices in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||
|
||||
// Process what's left and add it to result list
|
||||
foreach (const NetworkDeviceInfo &info, m_networkDeviceInfos) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info;
|
||||
}
|
||||
|
||||
// Create valid infos from the ping cache and offer them in the virtual infos
|
||||
foreach (const NetworkDeviceInfo &info, m_pingCache) {
|
||||
NetworkDeviceInfo finalInfo = info;
|
||||
finalInfo.setAddress(finalInfo.address());
|
||||
finalInfo.setHostName(finalInfo.hostName());
|
||||
finalInfo.setMacAddress(finalInfo.macAddress());
|
||||
finalInfo.setNetworkInterface(finalInfo.networkInterface());
|
||||
finalInfo.setMacAddressManufacturer(finalInfo.macAddressManufacturer());
|
||||
m_virtualNetworkDeviceInfos.append(info);
|
||||
}
|
||||
|
||||
m_virtualNetworkDeviceInfos.sortNetworkDevices();
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Virtual hosts (" << m_virtualNetworkDeviceInfos.count() << ")";
|
||||
foreach (const NetworkDeviceInfo &info, m_virtualNetworkDeviceInfos) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info;
|
||||
}
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Rest:";
|
||||
foreach (const MacAddress &macAddress, m_networkDeviceCache.keys()) {
|
||||
if (m_networkDeviceInfos.hasMacAddress(macAddress))
|
||||
continue;
|
||||
|
||||
NetworkDeviceInfo info = m_networkDeviceCache.value(macAddress);
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info << "Valid:" << info.isValid() << "Complete:" << info.isComplete() << info.incompleteProperties();
|
||||
}
|
||||
|
||||
emit finished();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef NETWORKDEVICEDISCOVERYREPLYIMPL_H
|
||||
#define NETWORKDEVICEDISCOVERYREPLYIMPL_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
#include "network/networkdeviceinfo.h"
|
||||
#include "network/networkdevicediscoveryreply.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class NetworkDeviceDiscoveryReplyImpl : public NetworkDeviceDiscoveryReply
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class NetworkDeviceDiscoveryImpl;
|
||||
|
||||
public:
|
||||
explicit NetworkDeviceDiscoveryReplyImpl(QObject *parent = nullptr);
|
||||
~NetworkDeviceDiscoveryReplyImpl() override = default;
|
||||
|
||||
NetworkDeviceInfos networkDeviceInfos() const override;
|
||||
NetworkDeviceInfos virtualNetworkDeviceInfos() const override;
|
||||
|
||||
private:
|
||||
NetworkDeviceInfos m_networkDeviceInfos; // Contains only complete and valid infos
|
||||
NetworkDeviceInfos m_virtualNetworkDeviceInfos; // Contains ping responses without ARP, like VPN devices
|
||||
|
||||
QHash<MacAddress, NetworkDeviceInfo> m_networkDeviceCache;
|
||||
qint64 m_startTimestamp;
|
||||
|
||||
// Temporary cache for ping responses where the mac is not known yet (like VPN devices)
|
||||
QHash<QHostAddress, NetworkDeviceInfo> m_pingCache;
|
||||
|
||||
QString macAddressFromHostAddress(const QHostAddress &address);
|
||||
bool hasHostAddress(const QHostAddress &address);
|
||||
|
||||
void verifyComplete(const MacAddress &macAddress);
|
||||
|
||||
// Add or update the network device info and verify if completed
|
||||
void processPingResponse(const QHostAddress &address, const QString &hostName);
|
||||
void processArpResponse(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||
void processMacManufacturer(const MacAddress &macAddress, const QString &manufacturer);
|
||||
|
||||
void processDiscoveryFinished();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NETWORKDEVICEDISCOVERYREPLYIMPL_H
|
||||
105
libnymea-core/hardware/network/networkdevicemonitorimpl.cpp
Normal file
105
libnymea-core/hardware/network/networkdevicemonitorimpl.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdevicemonitorimpl.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
NetworkDeviceMonitorImpl::NetworkDeviceMonitorImpl(const MacAddress &macAddress, QObject *parent) :
|
||||
NetworkDeviceMonitor(parent),
|
||||
m_macAddress(macAddress)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetworkDeviceMonitorImpl::~NetworkDeviceMonitorImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MacAddress NetworkDeviceMonitorImpl::macAddress() const
|
||||
{
|
||||
return m_macAddress;
|
||||
}
|
||||
|
||||
NetworkDeviceInfo NetworkDeviceMonitorImpl::networkDeviceInfo() const
|
||||
{
|
||||
return m_networkDeviceInfo;
|
||||
}
|
||||
|
||||
void NetworkDeviceMonitorImpl::setNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo)
|
||||
{
|
||||
if (m_networkDeviceInfo == networkDeviceInfo)
|
||||
return;
|
||||
|
||||
m_networkDeviceInfo = networkDeviceInfo;
|
||||
emit networkDeviceInfoChanged(m_networkDeviceInfo);
|
||||
}
|
||||
|
||||
bool NetworkDeviceMonitorImpl::reachable() const
|
||||
{
|
||||
return m_reachable;
|
||||
}
|
||||
|
||||
void NetworkDeviceMonitorImpl::setReachable(bool reachable)
|
||||
{
|
||||
if (m_reachable == reachable)
|
||||
return;
|
||||
|
||||
m_reachable = reachable;
|
||||
emit reachableChanged(m_reachable);
|
||||
}
|
||||
|
||||
QDateTime NetworkDeviceMonitorImpl::lastSeen() const
|
||||
{
|
||||
return m_lastSeen;
|
||||
}
|
||||
|
||||
void NetworkDeviceMonitorImpl::setLastSeen(const QDateTime &lastSeen)
|
||||
{
|
||||
if (m_lastSeen == lastSeen)
|
||||
return;
|
||||
|
||||
m_lastSeen = lastSeen;
|
||||
|
||||
emit lastSeenChanged(m_lastSeen);
|
||||
}
|
||||
|
||||
QDateTime NetworkDeviceMonitorImpl::lastConnectionAttempt() const
|
||||
{
|
||||
return m_lastConnectionAttempt;
|
||||
}
|
||||
|
||||
void NetworkDeviceMonitorImpl::setLastConnectionAttempt(const QDateTime &lastConnectionAttempt)
|
||||
{
|
||||
m_lastConnectionAttempt = lastConnectionAttempt;
|
||||
}
|
||||
|
||||
}
|
||||
74
libnymea-core/hardware/network/networkdevicemonitorimpl.h
Normal file
74
libnymea-core/hardware/network/networkdevicemonitorimpl.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef NETWORKDEVICEMONITORIMPL_H
|
||||
#define NETWORKDEVICEMONITORIMPL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "network/networkdevicemonitor.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class NetworkDeviceMonitorImpl : public NetworkDeviceMonitor
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NetworkDeviceMonitorImpl(const MacAddress &macAddress, QObject *parent = nullptr);
|
||||
~NetworkDeviceMonitorImpl();
|
||||
|
||||
MacAddress macAddress() const override;
|
||||
|
||||
NetworkDeviceInfo networkDeviceInfo() const override;
|
||||
void setNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
|
||||
|
||||
bool reachable() const override;
|
||||
void setReachable(bool reachable);
|
||||
|
||||
QDateTime lastSeen() const override;
|
||||
void setLastSeen(const QDateTime &lastSeen);
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NETWORKDEVICEMONITORIMPL_H
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
#include "hardware/modbus/modbusrtumanager.h"
|
||||
#include "hardware/modbus/modbusrtuhardwareresourceimplementation.h"
|
||||
#include "network/networkdevicediscovery.h"
|
||||
#include "hardware/network/networkdevicediscoveryimpl.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
@ -79,7 +79,7 @@ HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform,
|
||||
|
||||
m_modbusRtuResource = new ModbusRtuHardwareResourceImplementation(modbusRtuManager, this);
|
||||
|
||||
m_networkDeviceDiscovery = new NetworkDeviceDiscovery(this);
|
||||
m_networkDeviceDiscovery = new NetworkDeviceDiscoveryImpl(this);
|
||||
|
||||
// Enable all the resources
|
||||
setResourceEnabled(m_pluginTimerManager, true);
|
||||
|
||||
@ -45,6 +45,7 @@ class ZigbeeManager;
|
||||
class ZigbeeHardwareResourceImplementation;
|
||||
class ModbusRtuManager;
|
||||
class ModbusRtuHardwareResourceImplementation;
|
||||
class NetworkDeviceDiscoveryImpl;
|
||||
|
||||
class HardwareManagerImplementation : public HardwareManager
|
||||
{
|
||||
@ -84,7 +85,7 @@ private:
|
||||
I2CManager *m_i2cManager = nullptr;
|
||||
ZigbeeHardwareResourceImplementation *m_zigbeeResource = nullptr;
|
||||
ModbusRtuHardwareResourceImplementation *m_modbusRtuResource = nullptr;
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
NetworkDeviceDiscoveryImpl *m_networkDeviceDiscovery = nullptr;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -61,6 +61,7 @@ RESOURCES += $$top_srcdir/icons.qrc \
|
||||
|
||||
|
||||
HEADERS += nymeacore.h \
|
||||
hardware/network/macaddressdatabasereplyimpl.h \
|
||||
hardware/serialport/serialportmonitor.h \
|
||||
integrations/apikeysprovidersloader.h \
|
||||
integrations/plugininfocache.h \
|
||||
@ -141,7 +142,11 @@ HEADERS += nymeacore.h \
|
||||
hardware/modbus/modbusrtumanager.h \
|
||||
hardware/modbus/modbusrtumasterimpl.h \
|
||||
hardware/modbus/modbusrtureplyimpl.h \
|
||||
hardware/network/macaddressdatabase.h \
|
||||
hardware/network/networkaccessmanagerimpl.h \
|
||||
hardware/network/networkdevicediscoveryimpl.h \
|
||||
hardware/network/networkdevicediscoveryreplyimpl.h \
|
||||
hardware/network/networkdevicemonitorimpl.h \
|
||||
hardware/network/upnp/upnpdiscoveryimplementation.h \
|
||||
hardware/network/upnp/upnpdiscoveryrequest.h \
|
||||
hardware/network/upnp/upnpdiscoveryreplyimplementation.h \
|
||||
@ -161,6 +166,7 @@ HEADERS += nymeacore.h \
|
||||
|
||||
|
||||
SOURCES += nymeacore.cpp \
|
||||
hardware/network/macaddressdatabasereplyimpl.cpp \
|
||||
hardware/serialport/serialportmonitor.cpp \
|
||||
integrations/apikeysprovidersloader.cpp \
|
||||
integrations/plugininfocache.cpp \
|
||||
@ -233,7 +239,11 @@ SOURCES += nymeacore.cpp \
|
||||
hardware/modbus/modbusrtumanager.cpp \
|
||||
hardware/modbus/modbusrtumasterimpl.cpp \
|
||||
hardware/modbus/modbusrtureplyimpl.cpp \
|
||||
hardware/network/macaddressdatabase.cpp \
|
||||
hardware/network/networkaccessmanagerimpl.cpp \
|
||||
hardware/network/networkdevicediscoveryimpl.cpp \
|
||||
hardware/network/networkdevicediscoveryreplyimpl.cpp \
|
||||
hardware/network/networkdevicemonitorimpl.cpp \
|
||||
hardware/network/upnp/upnpdiscoveryimplementation.cpp \
|
||||
hardware/network/upnp/upnpdiscoveryrequest.cpp \
|
||||
hardware/network/upnp/upnpdiscoveryreplyimplementation.cpp \
|
||||
|
||||
@ -44,11 +44,13 @@ HEADERS += \
|
||||
network/apikeys/apikeysprovider.h \
|
||||
network/apikeys/apikeystorage.h \
|
||||
network/arpsocket.h \
|
||||
network/macaddressdatabase.h \
|
||||
network/macaddress.h \
|
||||
network/macaddressdatabasereply.h \
|
||||
network/networkdevicediscovery.h \
|
||||
network/networkdevicediscoveryreply.h \
|
||||
network/networkdeviceinfo.h \
|
||||
network/networkdeviceinfos.h \
|
||||
network/networkdevicemonitor.h \
|
||||
network/networkutils.h \
|
||||
network/ping.h \
|
||||
network/pingreply.h \
|
||||
@ -149,11 +151,12 @@ SOURCES += \
|
||||
network/apikeys/apikeysprovider.cpp \
|
||||
network/apikeys/apikeystorage.cpp \
|
||||
network/arpsocket.cpp \
|
||||
network/macaddressdatabase.cpp \
|
||||
network/macaddress.cpp \
|
||||
network/macaddressdatabasereply.cpp \
|
||||
network/networkdevicediscovery.cpp \
|
||||
network/networkdevicediscoveryreply.cpp \
|
||||
network/networkdeviceinfo.cpp \
|
||||
network/networkdeviceinfos.cpp \
|
||||
network/networkdevicemonitor.cpp \
|
||||
network/networkutils.cpp \
|
||||
network/ping.cpp \
|
||||
network/pingreply.cpp \
|
||||
|
||||
@ -85,16 +85,14 @@ bool ArpSocket::sendRequest(const QString &interfaceName)
|
||||
if (!m_isOpen)
|
||||
return false;
|
||||
|
||||
|
||||
// Get the interface
|
||||
qCDebug(dcArpSocket()) << "Sending ARP request to all network interfaces" << interfaceName << "...";
|
||||
qCDebug(dcArpSocket()) << "Sending ARP request to network interface" << interfaceName << "...";
|
||||
QNetworkInterface networkInterface = QNetworkInterface::interfaceFromName(interfaceName);
|
||||
if (!networkInterface.isValid()) {
|
||||
qCWarning(dcArpSocket()) << "Failed to send the ARP request to network interface" << interfaceName << "because the interface is not valid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
loadArpCache(networkInterface);
|
||||
return sendRequest(networkInterface);
|
||||
}
|
||||
|
||||
@ -107,7 +105,7 @@ bool ArpSocket::sendRequest(const QNetworkInterface &networkInterface)
|
||||
if (networkInterface.flags().testFlag(QNetworkInterface::IsLoopBack))
|
||||
return false;
|
||||
|
||||
// If have no interface indes, we cannot use this network
|
||||
// If the interface index is unknown, we cannot use this network
|
||||
if (networkInterface.index() == 0) {
|
||||
qCDebug(dcArpSocket()) << "Failed to send the ARP request to network interface" << networkInterface.name() << "because the system interface index is unknown.";
|
||||
return false;
|
||||
@ -130,8 +128,6 @@ bool ArpSocket::sendRequest(const QNetworkInterface &networkInterface)
|
||||
return false;
|
||||
}
|
||||
|
||||
loadArpCache(networkInterface);
|
||||
|
||||
qCDebug(dcArpSocket()) << "Verifying network interface" << networkInterface.name() << networkInterface.hardwareAddress() << "...";
|
||||
foreach (const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
|
||||
// Only IPv4
|
||||
@ -159,7 +155,7 @@ bool ArpSocket::sendRequest(const QNetworkInterface &networkInterface)
|
||||
if (targetAddress == entry.ip())
|
||||
continue;
|
||||
|
||||
sendRequestInternally(networkInterface.index(), networkInterface.hardwareAddress(), entry.ip(), "ff:ff:ff:ff:ff:ff", targetAddress);
|
||||
sendRequestInternally(networkInterface.index(), MacAddress(networkInterface.hardwareAddress()), entry.ip(), MacAddress::broadcast(), targetAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,8 +179,11 @@ bool ArpSocket::sendRequest(const QHostAddress &targetAddress)
|
||||
if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol)
|
||||
continue;
|
||||
|
||||
if (targetAddress.isInSubnet(entry.ip(), entry.netmask().toIPv4Address())) {
|
||||
return sendRequestInternally(networkInterface.index(), networkInterface.hardwareAddress(), entry.ip(), "ff:ff:ff:ff:ff:ff", targetAddress);
|
||||
qCDebug(dcArpSocket()) << "Check subnet for" << networkInterface.name() << entry.ip() << entry.netmask();
|
||||
if (targetAddress.isInSubnet(entry.ip(), entry.prefixLength())) {
|
||||
return sendRequestInternally(networkInterface.index(), MacAddress(networkInterface.hardwareAddress()), entry.ip(), MacAddress::broadcast(), targetAddress);
|
||||
} else {
|
||||
qCDebug(dcArpSocket()) << targetAddress << "is not part of subnet" << entry.ip() << "netmask" << entry.netmask() << "netmask int" << entry.netmask().toIPv4Address();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,66 +227,17 @@ bool ArpSocket::openSocket()
|
||||
return;
|
||||
|
||||
// Make sure to read all data from the socket...
|
||||
while (true) {
|
||||
char receiveBuffer[ETHER_ARP_PACKET_LEN];
|
||||
int bytesReceived = 0;
|
||||
while (bytesReceived >= 0) {
|
||||
unsigned char receiveBuffer[ETHER_ARP_PACKET_LEN];
|
||||
memset(&receiveBuffer, 0, sizeof(receiveBuffer));
|
||||
|
||||
// Read the buffer
|
||||
int bytesReceived = recv(m_socketDescriptor, receiveBuffer, ETHER_ARP_PACKET_LEN, 0);
|
||||
if (bytesReceived < 0) {
|
||||
// Finished reading
|
||||
return;
|
||||
}
|
||||
bytesReceived = recv(m_socketDescriptor, receiveBuffer, ETHER_ARP_PACKET_LEN, 0);
|
||||
if (bytesReceived < 0)
|
||||
continue;
|
||||
|
||||
// Parse data using structs header + arp
|
||||
struct ether_header *etherHeader = (struct ether_header *)(receiveBuffer);
|
||||
struct ether_arp *arpPacket = (struct ether_arp *)(receiveBuffer + ETHER_HEADER_LEN);
|
||||
QString senderMacAddress = getMacAddressString(arpPacket->arp_sha);
|
||||
QHostAddress senderHostAddress = getHostAddressString(arpPacket->arp_spa);
|
||||
QString targetMacAddress = getMacAddressString(arpPacket->arp_tha);
|
||||
QHostAddress targetHostAddress = getHostAddressString(arpPacket->arp_tpa);
|
||||
uint16_t etherType = htons(etherHeader->ether_type);
|
||||
if (etherType != ETHERTYPE_ARP) {
|
||||
qCWarning(dcArpSocketTraffic()) << "Received ARP socket data header with invalid type" << etherType;
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter for ARP replies
|
||||
uint16_t arpOperationCode = htons(arpPacket->arp_op);
|
||||
switch (arpOperationCode) {
|
||||
case ARPOP_REQUEST:
|
||||
//qCDebug(dcArpSocket()) << "ARP request from " << senderMacAddress << senderHostAddress.toString() << "-->" << targetMacAddress << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_REPLY: {
|
||||
QNetworkInterface networkInterface = NetworkUtils::getInterfaceForMacAddress(targetMacAddress);
|
||||
if (!networkInterface.isValid()) {
|
||||
qCWarning(dcArpSocket()) << "Could not find interface from ARP response" << targetHostAddress.toString() << targetMacAddress;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcArpSocketTraffic()) << "ARP response from" << senderMacAddress << senderHostAddress.toString() << "on" << networkInterface.name();
|
||||
emit arpResponse(networkInterface, senderHostAddress, senderMacAddress.toLower());
|
||||
break;
|
||||
}
|
||||
case ARPOP_RREQUEST:
|
||||
qCDebug(dcArpSocketTraffic()) << "RARP request from" << senderMacAddress << senderHostAddress.toString() << "-->" << targetMacAddress << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_RREPLY:
|
||||
qCDebug(dcArpSocketTraffic()) << "PARP response from" << senderMacAddress << senderHostAddress.toString() << "-->" << targetMacAddress << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_InREQUEST:
|
||||
qCDebug(dcArpSocketTraffic()) << "InARP request from" << senderMacAddress << senderHostAddress.toString() << "-->" << targetMacAddress << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_InREPLY:
|
||||
qCDebug(dcArpSocketTraffic()) << "InARP response from" << senderMacAddress << senderHostAddress.toString() << "-->" << targetMacAddress << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_NAK:
|
||||
qCDebug(dcArpSocketTraffic()) << "(ATM)ARP NAK from" << senderMacAddress << senderHostAddress.toString() << "-->" << targetMacAddress << targetHostAddress.toString();
|
||||
break;
|
||||
default:
|
||||
qCWarning(dcArpSocketTraffic()) << "Received unhandled ARP operation code" << arpOperationCode << "from" << senderMacAddress << senderHostAddress.toString();
|
||||
break;
|
||||
}
|
||||
processDataBuffer(receiveBuffer, bytesReceived);
|
||||
}
|
||||
});
|
||||
|
||||
@ -318,7 +268,7 @@ void ArpSocket::closeSocket()
|
||||
qCDebug(dcArpSocket()) << "ARP disabled successfully";
|
||||
}
|
||||
|
||||
bool ArpSocket::sendRequestInternally(int networkInterfaceIndex, const QString &senderMacAddress, const QHostAddress &senderHostAddress, const QString &targetMacAddress, const QHostAddress &targetHostAddress)
|
||||
bool ArpSocket::sendRequestInternally(int networkInterfaceIndex, const MacAddress &senderMacAddress, const QHostAddress &senderHostAddress, const MacAddress &targetMacAddress, const QHostAddress &targetHostAddress)
|
||||
{
|
||||
// Set up data structures
|
||||
unsigned char sendingBuffer[ETHER_ARP_PACKET_LEN];
|
||||
@ -364,14 +314,80 @@ bool ArpSocket::sendRequestInternally(int networkInterfaceIndex, const QString &
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ArpSocket::getMacAddressString(uint8_t *senderHardwareAddress)
|
||||
void ArpSocket::processDataBuffer(unsigned char *receiveBuffer, int size)
|
||||
{
|
||||
QStringList hexValues;
|
||||
for (int i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||
hexValues.append(QString("%1").arg(senderHardwareAddress[i], 2, 16, QLatin1Char('0')));
|
||||
// Parse data using structs header + arp
|
||||
QByteArray receivedBufferBytes;
|
||||
for (int i = 0; i < size; i++) {
|
||||
receivedBufferBytes.append(receiveBuffer[i]);
|
||||
}
|
||||
|
||||
return hexValues.join(":");
|
||||
struct ether_header *etherHeader = (struct ether_header *)(receiveBuffer);
|
||||
struct ether_arp *arpPacket = (struct ether_arp *)(receiveBuffer + ETHER_HEADER_LEN);
|
||||
MacAddress senderMacAddress = MacAddress(arpPacket->arp_sha);
|
||||
QHostAddress senderHostAddress = getHostAddressString(arpPacket->arp_spa);
|
||||
MacAddress targetMacAddress = MacAddress(arpPacket->arp_tha);
|
||||
QHostAddress targetHostAddress = getHostAddressString(arpPacket->arp_tpa);
|
||||
|
||||
uint16_t etherType = htons(etherHeader->ether_type);
|
||||
if (etherType != ETHERTYPE_ARP) {
|
||||
qCWarning(dcArpSocketTraffic()) << "Received ARP socket data header with invalid type" << etherType;
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter for ARP replies
|
||||
uint16_t arpOperationCode = htons(arpPacket->arp_op);
|
||||
switch (arpOperationCode) {
|
||||
case ARPOP_REQUEST: {
|
||||
// The sender of the arp request provides ip and mac.
|
||||
// Lets find the corresponding interface and use it for the discovery and monitor
|
||||
if (senderHostAddress.isNull())
|
||||
return;
|
||||
|
||||
QNetworkInterface networkInterface = NetworkUtils::getInterfaceForHostaddress(senderHostAddress);
|
||||
if (!networkInterface.isValid()) {
|
||||
qCDebug(dcArpSocket()) << "Could not find local interface from ARP request" << senderHostAddress.toString() << senderMacAddress.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: we are not interested in our own requests
|
||||
if (senderMacAddress != MacAddress(networkInterface.hardwareAddress())) {
|
||||
qCDebug(dcArpSocket()) << "ARP request" << receivedBufferBytes.toHex() << "from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString() << "on" << networkInterface.name();
|
||||
emit arpRequestReceived(networkInterface, senderHostAddress, senderMacAddress);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ARPOP_REPLY: {
|
||||
QNetworkInterface networkInterface = NetworkUtils::getInterfaceForMacAddress(targetMacAddress);
|
||||
if (!networkInterface.isValid()) {
|
||||
qCDebug(dcArpSocket()) << "Could not find local interface from ARP response" << targetHostAddress.toString() << targetMacAddress.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcArpSocket()) << "ARP reply" << receivedBufferBytes.toHex() << "from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString() << "on" << networkInterface.name();
|
||||
emit arpResponse(networkInterface, senderHostAddress, senderMacAddress);
|
||||
break;
|
||||
}
|
||||
case ARPOP_RREQUEST:
|
||||
qCDebug(dcArpSocketTraffic()) << "RARP request from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_RREPLY:
|
||||
qCDebug(dcArpSocketTraffic()) << "PARP response from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_InREQUEST:
|
||||
qCDebug(dcArpSocketTraffic()) << "InARP request from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_InREPLY:
|
||||
qCDebug(dcArpSocketTraffic()) << "InARP response from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString();
|
||||
break;
|
||||
case ARPOP_NAK:
|
||||
qCDebug(dcArpSocketTraffic()) << "(ATM)ARP NAK from" << senderMacAddress.toString() << senderHostAddress.toString() << "-->" << targetMacAddress.toString() << targetHostAddress.toString();
|
||||
break;
|
||||
default:
|
||||
qCWarning(dcArpSocketTraffic()) << "Received unhandled ARP operation code" << arpOperationCode << "from" << senderMacAddress.toString() << senderHostAddress.toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QHostAddress ArpSocket::getHostAddressString(uint8_t *senderIpAddress)
|
||||
@ -403,52 +419,54 @@ bool ArpSocket::loadArpCache(const QNetworkInterface &interface)
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
lineCount += 1;
|
||||
|
||||
// Skip the first line since it's just the header
|
||||
if (lineCount == 0)
|
||||
continue;
|
||||
|
||||
|
||||
//qCDebug(dcArpSocket()) << "Checking line" << line;
|
||||
|
||||
qCDebug(dcArpSocket()) << "Checking line" << line;
|
||||
QStringList columns = line.split(QLatin1Char(' '));
|
||||
columns.removeAll("");
|
||||
|
||||
// Make sure we have enought token
|
||||
if (columns.count() < 6) {
|
||||
qCWarning(dcArpSocket()) << "Line has invalid column count" << line;
|
||||
qCWarning(dcArpSocket()) << "ARP cache line has invalid column count" << line;
|
||||
continue;
|
||||
}
|
||||
|
||||
QHostAddress address(columns.at(0).trimmed());
|
||||
if (address.isNull()) {
|
||||
qCWarning(dcArpSocket()) << "Line has invalid address";
|
||||
qCWarning(dcArpSocket()) << "ARP cache line has invalid IP address";
|
||||
continue;
|
||||
}
|
||||
|
||||
QString macAddress = columns.at(3).trimmed();
|
||||
if (macAddress.count() != 17) {
|
||||
qCWarning(dcArpSocket()) << "Line has invalid mac address" << columns << macAddress;
|
||||
QString macAddressString = columns.at(3).trimmed();
|
||||
MacAddress macAddress(macAddressString);
|
||||
if (macAddress.isNull()) {
|
||||
qCDebug(dcArpSocket()) << "ARP cache line has invalid MAC address" << macAddressString;
|
||||
continue;
|
||||
}
|
||||
|
||||
QNetworkInterface addressInterface = QNetworkInterface::interfaceFromName(columns.at(5));
|
||||
if (!addressInterface.isValid())
|
||||
if (!addressInterface.isValid()) {
|
||||
qCDebug(dcArpSocket()) << "ARP cache line has invalid network interface identifier" << columns << columns.at(5);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we filter for specific interfaces
|
||||
if (interface.isValid() && addressInterface.name() != interface.name())
|
||||
continue;
|
||||
|
||||
qCDebug(dcArpSocket()) << "Loaded from cache" << address.toString() << macAddress << addressInterface.name();
|
||||
qCDebug(dcArpSocket()) << "Loaded from cache" << address.toString() << macAddress.toString() << addressInterface.name();
|
||||
emit arpResponse(addressInterface, address, macAddress);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArpSocket::fillMacAddress(uint8_t *targetArray, const QString &macAddress)
|
||||
void ArpSocket::fillMacAddress(uint8_t *targetArray, const MacAddress &macAddress)
|
||||
{
|
||||
QStringList macValues = macAddress.split(":");
|
||||
QStringList macValues = macAddress.toString().split(":");
|
||||
for (int i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||
targetArray[i] = macValues.at(i).toUInt(nullptr, 16);
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
#include <QNetworkInterface>
|
||||
|
||||
#include "libnymea.h"
|
||||
#include "macaddress.h"
|
||||
|
||||
class LIBNYMEA_EXPORT ArpSocket : public QObject
|
||||
{
|
||||
@ -64,21 +65,23 @@ public:
|
||||
void closeSocket();
|
||||
|
||||
signals:
|
||||
void arpResponse(const QNetworkInterface &networkInterface, const QHostAddress &address, const QString &macAddress);
|
||||
void arpResponse(const QNetworkInterface &networkInterface, const QHostAddress &address, const MacAddress &macAddress);
|
||||
void arpRequestReceived(const QNetworkInterface &networkInterface, const QHostAddress &address, const MacAddress &macAddress);
|
||||
|
||||
private:
|
||||
QSocketNotifier *m_socketNotifier = nullptr;
|
||||
int m_socketDescriptor = -1;
|
||||
bool m_isOpen = false;
|
||||
|
||||
bool sendRequestInternally(int networkInterfaceIndex, const QString &senderMacAddress, const QHostAddress &senderHostAddress, const QString &targetMacAddress, const QHostAddress &targetHostAddress);
|
||||
bool sendRequestInternally(int networkInterfaceIndex, const MacAddress &senderMacAddress, const QHostAddress &senderHostAddress, const MacAddress &targetMacAddress, const QHostAddress &targetHostAddress);
|
||||
|
||||
void processDataBuffer(unsigned char *receiveBuffer, int size);
|
||||
|
||||
QString getMacAddressString(uint8_t *senderHardwareAddress);
|
||||
QHostAddress getHostAddressString(uint8_t *senderIpAddress);
|
||||
|
||||
bool loadArpCache(const QNetworkInterface &interface = QNetworkInterface());
|
||||
|
||||
void fillMacAddress(uint8_t *targetArray, const QString &macAddress);
|
||||
void fillMacAddress(uint8_t *targetArray, const MacAddress &macAddress);
|
||||
void fillHostAddress(uint8_t *targetArray, const QHostAddress &hostAddress);
|
||||
|
||||
};
|
||||
|
||||
162
libnymea/network/macaddress.cpp
Normal file
162
libnymea/network/macaddress.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "macaddress.h"
|
||||
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
|
||||
MacAddress::MacAddress()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(const QString &macAddress)
|
||||
{
|
||||
QString addressString;
|
||||
|
||||
// Filter out any non hex characters from the string (like any separators)
|
||||
QRegularExpression hexMatcher("^[0-9A-F]", QRegularExpression::CaseInsensitiveOption);
|
||||
for (int i = 0; i < macAddress.count(); i++) {
|
||||
// Remove all possible separators
|
||||
QRegularExpressionMatch match = hexMatcher.match(macAddress.at(i));
|
||||
if (match.hasMatch()) {
|
||||
addressString.append(macAddress.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining hex value has to be 12 characters (6 hex values)
|
||||
if (addressString.size() == 12) {
|
||||
m_rawData = QByteArray::fromHex(addressString.toUtf8());
|
||||
} else {
|
||||
// Make invalid and null
|
||||
m_rawData.clear();
|
||||
}
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(const QByteArray &macAddress)
|
||||
{
|
||||
m_rawData = macAddress;
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(unsigned char *rawData)
|
||||
{
|
||||
clear();
|
||||
for (int i = 0; i < ETH_ALEN; i++) {
|
||||
m_rawData[i] = rawData[i];
|
||||
}
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(const MacAddress &other)
|
||||
{
|
||||
m_rawData = other.toByteArray();
|
||||
}
|
||||
|
||||
MacAddress MacAddress::broadcast()
|
||||
{
|
||||
return MacAddress(QByteArray::fromHex("ffffffffffff"));
|
||||
}
|
||||
|
||||
MacAddress MacAddress::fromString(const QString &macAddress)
|
||||
{
|
||||
return MacAddress(macAddress);
|
||||
}
|
||||
|
||||
QString MacAddress::toString() const
|
||||
{
|
||||
QString macString(QStringLiteral("%1:%2:%3:%4:%5:%6"));
|
||||
QByteArray data = m_rawData;
|
||||
if (!isValid()) {
|
||||
data = QByteArray(ETH_ALEN, '\0');
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
macString = macString.arg(static_cast<quint8>(data.at(i)), 2, 16, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
return macString.toLower();
|
||||
}
|
||||
|
||||
QByteArray MacAddress::toByteArray() const
|
||||
{
|
||||
return m_rawData;
|
||||
}
|
||||
|
||||
bool MacAddress::isNull() const
|
||||
{
|
||||
if (!isValid())
|
||||
return true;
|
||||
|
||||
return m_rawData == QByteArray(ETH_ALEN, '\0');
|
||||
}
|
||||
|
||||
bool MacAddress::isValid() const
|
||||
{
|
||||
return m_rawData.size() == ETH_ALEN;
|
||||
}
|
||||
|
||||
void MacAddress::clear()
|
||||
{
|
||||
m_rawData.fill('\0', ETH_ALEN);
|
||||
}
|
||||
|
||||
MacAddress &MacAddress::operator=(const MacAddress &other)
|
||||
{
|
||||
m_rawData = other.toByteArray();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool MacAddress::operator<(const MacAddress &other) const
|
||||
{
|
||||
return m_rawData < other.toByteArray();
|
||||
}
|
||||
|
||||
bool MacAddress::operator>(const MacAddress &other) const
|
||||
{
|
||||
return m_rawData > other.toByteArray();
|
||||
}
|
||||
|
||||
bool MacAddress::operator==(const MacAddress &other) const
|
||||
{
|
||||
return m_rawData == other.toByteArray();
|
||||
}
|
||||
|
||||
bool MacAddress::operator!=(const MacAddress &other) const
|
||||
{
|
||||
return m_rawData != other.toByteArray();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const MacAddress &macAddress)
|
||||
{
|
||||
debug.nospace() << "MacAddress(" << macAddress.toString() << ")";
|
||||
return debug.space();
|
||||
}
|
||||
86
libnymea/network/macaddress.h
Normal file
86
libnymea/network/macaddress.h
Normal file
@ -0,0 +1,86 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef MACADDRESS_H
|
||||
#define MACADDRESS_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "libnymea.h"
|
||||
|
||||
class LIBNYMEA_EXPORT MacAddress
|
||||
{
|
||||
public:
|
||||
explicit MacAddress();
|
||||
explicit MacAddress(const QString &macAddress);
|
||||
explicit MacAddress(const QByteArray &macAddress);
|
||||
explicit MacAddress(unsigned char *rawData);
|
||||
MacAddress(const MacAddress &other);
|
||||
|
||||
static MacAddress broadcast();
|
||||
static MacAddress fromString(const QString &macAddress);
|
||||
|
||||
QString toString() const;
|
||||
|
||||
QByteArray toByteArray() const;
|
||||
|
||||
bool isNull() const;
|
||||
bool isValid() const;
|
||||
|
||||
void clear();
|
||||
|
||||
MacAddress &operator=(const MacAddress &other);
|
||||
|
||||
bool operator<(const MacAddress &other) const;
|
||||
bool operator>(const MacAddress &other) const;
|
||||
bool operator==(const MacAddress &other) const;
|
||||
bool operator!=(const MacAddress &other) const;
|
||||
|
||||
private:
|
||||
QByteArray m_rawData = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#if QT_VERSION < 0x0600000
|
||||
using qhash_result_t = uint;
|
||||
#else
|
||||
using qhash_result_t = size_t;
|
||||
#endif
|
||||
inline qhash_result_t qHash(const MacAddress &macAddress, qhash_result_t seed)
|
||||
{
|
||||
return qHash(macAddress.toByteArray(), seed);
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const MacAddress &address);
|
||||
|
||||
#endif // MACADDRESS_H
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -28,15 +28,9 @@
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdevicediscoveryreply.h"
|
||||
#include "macaddressdatabasereply.h"
|
||||
|
||||
NetworkDeviceDiscoveryReply::NetworkDeviceDiscoveryReply(QObject *parent) :
|
||||
QObject(parent)
|
||||
MacAddressDatabaseReply::MacAddressDatabaseReply(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetworkDeviceInfos &NetworkDeviceDiscoveryReply::networkDeviceInfos()
|
||||
{
|
||||
return m_networkDeviceInfos;
|
||||
}
|
||||
54
libnymea/network/macaddressdatabasereply.h
Normal file
54
libnymea/network/macaddressdatabasereply.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef MACADDRESSDATABASEREPLY_H
|
||||
#define MACADDRESSDATABASEREPLY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "libnymea.h"
|
||||
|
||||
class LIBNYMEA_EXPORT MacAddressDatabaseReply : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MacAddressDatabaseReply(QObject *parent = nullptr);
|
||||
virtual ~MacAddressDatabaseReply() = default;
|
||||
|
||||
virtual QString macAddress() const = 0;
|
||||
virtual QString manufacturer() const = 0;
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
};
|
||||
|
||||
#endif // MACADDRESSDATABASEREPLY_H
|
||||
@ -42,7 +42,7 @@
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
class LIBNYMEA_EXPORT NetworkAccessManager : public HardwareResource
|
||||
class LIBNYMEA_EXPORT NetworkAccessManager : public HardwareResource
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -29,242 +29,10 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdevicediscovery.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "networkutils.h"
|
||||
#include "macaddressdatabase.h"
|
||||
#include "arpsocket.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
NYMEA_LOGGING_CATEGORY(dcNetworkDeviceDiscovery, "NetworkDeviceDiscovery")
|
||||
|
||||
NetworkDeviceDiscovery::NetworkDeviceDiscovery(QObject *parent) :
|
||||
QObject(parent)
|
||||
HardwareResource("Network device discovery", parent)
|
||||
{
|
||||
// Create ARP socket
|
||||
m_arpSocket = new ArpSocket(this);
|
||||
connect(m_arpSocket, &ArpSocket::arpResponse, this, &NetworkDeviceDiscovery::onArpResponseRceived);
|
||||
bool arpAvailable = m_arpSocket->openSocket();
|
||||
if (!arpAvailable) {
|
||||
m_arpSocket->closeSocket();
|
||||
}
|
||||
|
||||
// Create ping socket
|
||||
m_ping = new Ping(this);
|
||||
if (!m_ping->available())
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Failed to create ping tool" << m_ping->error();
|
||||
|
||||
// Init MAC database if available
|
||||
m_macAddressDatabase = new MacAddressDatabase(this);
|
||||
|
||||
// Timer for max duration af a discovery
|
||||
m_discoveryTimer = new QTimer(this);
|
||||
m_discoveryTimer->setInterval(20000);
|
||||
m_discoveryTimer->setSingleShot(true);
|
||||
connect(m_discoveryTimer, &QTimer::timeout, this, [=](){
|
||||
if (m_runningPingRepies.isEmpty() && m_currentReply) {
|
||||
finishDiscovery();
|
||||
}
|
||||
});
|
||||
|
||||
if (!arpAvailable && !m_ping->available()) {
|
||||
qCWarning(dcNetworkDeviceDiscovery()) << "Network device discovery is not available on this system.";
|
||||
} else {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Created successfully";
|
||||
}
|
||||
}
|
||||
|
||||
NetworkDeviceDiscoveryReply *NetworkDeviceDiscovery::discover()
|
||||
{
|
||||
if (m_currentReply) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Discovery already running. Returning current pending discovery reply...";
|
||||
return m_currentReply;
|
||||
}
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Starting network device discovery ...";
|
||||
NetworkDeviceDiscoveryReply *reply = new NetworkDeviceDiscoveryReply(this);
|
||||
m_currentReply = reply;
|
||||
m_currentReply->m_startTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
if (m_ping->available()) {
|
||||
pingAllNetworkDevices();
|
||||
}
|
||||
|
||||
if (m_arpSocket->isOpen()) {
|
||||
m_arpSocket->sendRequest();
|
||||
}
|
||||
|
||||
m_discoveryTimer->start();
|
||||
m_running = true;
|
||||
emit runningChanged(m_running);
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscovery::available() const
|
||||
{
|
||||
return m_arpSocket->isOpen() || m_ping->available();
|
||||
}
|
||||
|
||||
bool NetworkDeviceDiscovery::running() const
|
||||
{
|
||||
return m_running;
|
||||
}
|
||||
|
||||
PingReply *NetworkDeviceDiscovery::ping(const QHostAddress &address)
|
||||
{
|
||||
return m_ping->ping(address);
|
||||
}
|
||||
|
||||
MacAddressDatabaseReply *NetworkDeviceDiscovery::lookupMacAddress(const QString &macAddress)
|
||||
{
|
||||
return m_macAddressDatabase->lookupMacAddress(macAddress);
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscovery::pingAllNetworkDevices()
|
||||
{
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Starting ping for all network devices...";
|
||||
foreach (const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
|
||||
if (networkInterface.flags().testFlag(QNetworkInterface::IsLoopBack))
|
||||
continue;
|
||||
|
||||
if (!networkInterface.flags().testFlag(QNetworkInterface::IsUp))
|
||||
continue;
|
||||
|
||||
if (!networkInterface.flags().testFlag(QNetworkInterface::IsRunning))
|
||||
continue;
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Verifying network interface" << networkInterface.name() << networkInterface.hardwareAddress() << "...";
|
||||
foreach (const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
|
||||
// Only IPv4
|
||||
if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol)
|
||||
continue;
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Host address:" << entry.ip().toString();
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Broadcast address:" << entry.broadcast().toString();
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Netmask:" << entry.netmask().toString();
|
||||
quint32 addressRangeStart = entry.ip().toIPv4Address() & entry.netmask().toIPv4Address();
|
||||
quint32 addressRangeStop = entry.broadcast().toIPv4Address() | addressRangeStart;
|
||||
quint32 range = addressRangeStop - addressRangeStart;
|
||||
|
||||
// Let's scan only 255.255.255.0 networks for now
|
||||
if (range > 255)
|
||||
continue;
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << " Address range" << range << " | from" << QHostAddress(addressRangeStart).toString() << "-->" << QHostAddress(addressRangeStop).toString();
|
||||
// Send ping request to each address within the range
|
||||
for (quint32 i = 1; i < range; i++) {
|
||||
quint32 address = addressRangeStart + i;
|
||||
QHostAddress targetAddress(address);
|
||||
|
||||
// Skip our self
|
||||
if (targetAddress == entry.ip())
|
||||
continue;
|
||||
|
||||
PingReply *reply = m_ping->ping(targetAddress);
|
||||
m_runningPingRepies.append(reply);
|
||||
connect(reply, &PingReply::finished, this, [=](){
|
||||
m_runningPingRepies.removeAll(reply);
|
||||
if (reply->error() == PingReply::ErrorNoError) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Ping response from" << targetAddress.toString() << reply->hostName() << reply->duration() << "ms";
|
||||
int index = m_currentReply->networkDeviceInfos().indexFromHostAddress(targetAddress);
|
||||
if (index < 0) {
|
||||
// Add the network device
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
networkDeviceInfo.setAddress(targetAddress);
|
||||
networkDeviceInfo.setHostName(reply->hostName());
|
||||
m_currentReply->networkDeviceInfos().append(networkDeviceInfo);
|
||||
} else {
|
||||
m_currentReply->networkDeviceInfos()[index].setAddress(targetAddress);
|
||||
m_currentReply->networkDeviceInfos()[index].setHostName(reply->hostName());
|
||||
if (!m_currentReply->networkDeviceInfos()[index].networkInterface().isValid()) {
|
||||
m_currentReply->networkDeviceInfos()[index].setNetworkInterface(NetworkUtils::getInterfaceForHostaddress(targetAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_runningPingRepies.isEmpty() && m_currentReply && !m_discoveryTimer->isActive()) {
|
||||
finishDiscovery();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscovery::finishDiscovery()
|
||||
{
|
||||
m_discoveryTimer->stop();
|
||||
m_running = false;
|
||||
emit runningChanged(m_running);
|
||||
|
||||
// Sort by host address
|
||||
m_currentReply->networkDeviceInfos().sortNetworkDevices();
|
||||
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_currentReply->m_startTimestamp;
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Discovery finished. Found" << m_currentReply->networkDeviceInfos().count() << "network devices in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||
emit m_currentReply->finished();
|
||||
m_currentReply->deleteLater();
|
||||
m_currentReply = nullptr;
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscovery::updateOrAddNetworkDeviceArp(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress, const QString &manufacturer)
|
||||
{
|
||||
if (!m_currentReply)
|
||||
return;
|
||||
|
||||
int index = m_currentReply->networkDeviceInfos().indexFromHostAddress(address);
|
||||
if (index >= 0) {
|
||||
// Update the network device
|
||||
m_currentReply->networkDeviceInfos()[index].setMacAddress(macAddress);
|
||||
if (!manufacturer.isEmpty())
|
||||
m_currentReply->networkDeviceInfos()[index].setMacAddressManufacturer(manufacturer);
|
||||
|
||||
if (interface.isValid()) {
|
||||
m_currentReply->networkDeviceInfos()[index].setNetworkInterface(interface);
|
||||
}
|
||||
} else {
|
||||
index = m_currentReply->networkDeviceInfos().indexFromMacAddress(macAddress);
|
||||
if (index >= 0) {
|
||||
// Update the network device
|
||||
m_currentReply->networkDeviceInfos()[index].setAddress(address);
|
||||
if (!manufacturer.isEmpty())
|
||||
m_currentReply->networkDeviceInfos()[index].setMacAddressManufacturer(manufacturer);
|
||||
|
||||
if (interface.isValid()) {
|
||||
m_currentReply->networkDeviceInfos()[index].setNetworkInterface(interface);
|
||||
}
|
||||
} else {
|
||||
// Add the network device
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
networkDeviceInfo.setAddress(address);
|
||||
networkDeviceInfo.setMacAddress(macAddress);
|
||||
if (!manufacturer.isEmpty())
|
||||
networkDeviceInfo.setMacAddressManufacturer(manufacturer);
|
||||
|
||||
if (interface.isValid())
|
||||
networkDeviceInfo.setNetworkInterface(interface);
|
||||
|
||||
m_currentReply->networkDeviceInfos().append(networkDeviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceDiscovery::onArpResponseRceived(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress)
|
||||
{
|
||||
if (!m_currentReply) {
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "Received ARP reply from" << address.toString() << macAddress << "but there is no discovery running.";
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "ARP reply received" << address.toString() << macAddress << interface.name();
|
||||
// Lookup the mac address vendor if possible
|
||||
if (m_macAddressDatabase->available()) {
|
||||
MacAddressDatabaseReply *reply = m_macAddressDatabase->lookupMacAddress(macAddress);
|
||||
connect(reply, &MacAddressDatabaseReply::finished, m_currentReply, [=](){
|
||||
qCDebug(dcNetworkDeviceDiscovery()) << "MAC manufacturer lookup finished for" << macAddress << ":" << reply->manufacturer();
|
||||
updateOrAddNetworkDeviceArp(interface, address, macAddress, reply->manufacturer());
|
||||
});
|
||||
} else {
|
||||
updateOrAddNetworkDeviceArp(interface, address, macAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -35,50 +35,41 @@
|
||||
#include <QObject>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "ping.h"
|
||||
#include "libnymea.h"
|
||||
#include "hardwareresource.h"
|
||||
|
||||
#include "networkdevicemonitor.h"
|
||||
|
||||
#include "pingreply.h"
|
||||
#include "macaddressdatabasereply.h"
|
||||
#include "networkdevicediscoveryreply.h"
|
||||
|
||||
class ArpSocket;
|
||||
class MacAddressDatabase;
|
||||
class MacAddressDatabaseReply;
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery)
|
||||
|
||||
class LIBNYMEA_EXPORT NetworkDeviceDiscovery : public QObject
|
||||
class LIBNYMEA_EXPORT NetworkDeviceDiscovery : public HardwareResource
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NetworkDeviceDiscovery(QObject *parent = nullptr);
|
||||
virtual ~NetworkDeviceDiscovery() = default;
|
||||
|
||||
NetworkDeviceDiscoveryReply *discover();
|
||||
virtual NetworkDeviceDiscoveryReply *discover() = 0;
|
||||
|
||||
bool available() const;
|
||||
bool running() const;
|
||||
virtual bool running() const = 0;
|
||||
|
||||
PingReply *ping(const QHostAddress &address);
|
||||
MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress);
|
||||
virtual NetworkDeviceMonitor *registerMonitor(const MacAddress &macAddress) = 0;
|
||||
|
||||
virtual void unregisterMonitor(const MacAddress &macAddress) = 0;
|
||||
virtual void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) = 0;
|
||||
|
||||
virtual PingReply *ping(const QHostAddress &address) = 0;
|
||||
|
||||
virtual MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) = 0;
|
||||
virtual MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) = 0;
|
||||
|
||||
virtual bool sendArpRequest(const QHostAddress &address) = 0;
|
||||
|
||||
signals:
|
||||
void runningChanged(bool running);
|
||||
|
||||
private:
|
||||
MacAddressDatabase *m_macAddressDatabase = nullptr;
|
||||
ArpSocket *m_arpSocket = nullptr;
|
||||
Ping *m_ping = nullptr;
|
||||
bool m_running = false;
|
||||
|
||||
QTimer *m_discoveryTimer = nullptr;
|
||||
NetworkDeviceDiscoveryReply *m_currentReply = nullptr;
|
||||
QList<PingReply *> m_runningPingRepies;
|
||||
|
||||
void pingAllNetworkDevices();
|
||||
void finishDiscovery();
|
||||
|
||||
void updateOrAddNetworkDeviceArp(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress, const QString &manufacturer = QString());
|
||||
|
||||
private slots:
|
||||
void onArpResponseRceived(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress);
|
||||
void networkDeviceInfoCacheUpdated();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -39,19 +39,21 @@
|
||||
class LIBNYMEA_EXPORT NetworkDeviceDiscoveryReply : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class NetworkDeviceDiscovery;
|
||||
|
||||
public:
|
||||
NetworkDeviceInfos &networkDeviceInfos();
|
||||
explicit NetworkDeviceDiscoveryReply(QObject *parent = nullptr) : QObject(parent) { };
|
||||
virtual ~NetworkDeviceDiscoveryReply() = default;
|
||||
|
||||
virtual NetworkDeviceInfos networkDeviceInfos() const = 0;
|
||||
virtual NetworkDeviceInfos virtualNetworkDeviceInfos() const = 0;
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
// Emitted whenever a certain host address has been pinged successfully
|
||||
void hostAddressDiscovered(const QHostAddress &address);
|
||||
|
||||
private:
|
||||
explicit NetworkDeviceDiscoveryReply(QObject *parent = nullptr);
|
||||
NetworkDeviceInfos m_networkDeviceInfos;
|
||||
qint64 m_startTimestamp;
|
||||
// Emited whenerver a valid NetworkDeviceInfo has been added
|
||||
void networkDeviceInfoAdded(const NetworkDeviceInfo &networkDeviceInfo);
|
||||
|
||||
void finished();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -29,6 +29,7 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdeviceinfo.h"
|
||||
#include "macaddress.h"
|
||||
|
||||
NetworkDeviceInfo::NetworkDeviceInfo()
|
||||
{
|
||||
@ -38,7 +39,13 @@ NetworkDeviceInfo::NetworkDeviceInfo()
|
||||
NetworkDeviceInfo::NetworkDeviceInfo(const QString &macAddress):
|
||||
m_macAddress(macAddress)
|
||||
{
|
||||
m_macAddressSet = true;
|
||||
}
|
||||
|
||||
NetworkDeviceInfo::NetworkDeviceInfo(const QHostAddress &address):
|
||||
m_address(address)
|
||||
{
|
||||
m_addressSet = true;
|
||||
}
|
||||
|
||||
QString NetworkDeviceInfo::macAddress() const
|
||||
@ -49,6 +56,7 @@ QString NetworkDeviceInfo::macAddress() const
|
||||
void NetworkDeviceInfo::setMacAddress(const QString &macAddress)
|
||||
{
|
||||
m_macAddress = macAddress;
|
||||
m_macAddressSet = true;
|
||||
}
|
||||
|
||||
QString NetworkDeviceInfo::macAddressManufacturer() const
|
||||
@ -59,6 +67,7 @@ QString NetworkDeviceInfo::macAddressManufacturer() const
|
||||
void NetworkDeviceInfo::setMacAddressManufacturer(const QString &macAddressManufacturer)
|
||||
{
|
||||
m_macAddressManufacturer = macAddressManufacturer;
|
||||
m_macAddressManufacturerSet = true;
|
||||
}
|
||||
|
||||
QHostAddress NetworkDeviceInfo::address() const
|
||||
@ -69,6 +78,7 @@ QHostAddress NetworkDeviceInfo::address() const
|
||||
void NetworkDeviceInfo::setAddress(const QHostAddress &address)
|
||||
{
|
||||
m_address = address;
|
||||
m_addressSet = true;
|
||||
}
|
||||
|
||||
QString NetworkDeviceInfo::hostName() const
|
||||
@ -79,6 +89,7 @@ QString NetworkDeviceInfo::hostName() const
|
||||
void NetworkDeviceInfo::setHostName(const QString &hostName)
|
||||
{
|
||||
m_hostName = hostName;
|
||||
m_hostNameSet = true;
|
||||
}
|
||||
|
||||
QNetworkInterface NetworkDeviceInfo::networkInterface() const
|
||||
@ -89,27 +100,60 @@ QNetworkInterface NetworkDeviceInfo::networkInterface() const
|
||||
void NetworkDeviceInfo::setNetworkInterface(const QNetworkInterface &networkInterface)
|
||||
{
|
||||
m_networkInterface = networkInterface;
|
||||
m_networkInterfaceSet = true;
|
||||
}
|
||||
|
||||
bool NetworkDeviceInfo::isValid() const
|
||||
{
|
||||
return (!m_address.isNull() || !m_macAddress.isEmpty()) && m_networkInterface.isValid();
|
||||
return (!m_address.isNull() || !MacAddress(m_macAddress).isNull()) && m_networkInterface.isValid();
|
||||
}
|
||||
|
||||
bool NetworkDeviceInfo::isComplete() const
|
||||
{
|
||||
return m_macAddressSet && m_macAddressManufacturerSet && m_addressSet && m_hostNameSet && m_networkInterfaceSet;
|
||||
}
|
||||
|
||||
QString NetworkDeviceInfo::incompleteProperties() const
|
||||
{
|
||||
QStringList list;
|
||||
if (!m_macAddressSet) list.append("MAC not set");
|
||||
if (!m_macAddressManufacturerSet) list.append("MAC vendor not set");
|
||||
if (!m_hostNameSet) list.append("hostname not set");
|
||||
if (!m_networkInterfaceSet) list.append("nework interface not set");
|
||||
return list.join(", ");
|
||||
}
|
||||
|
||||
bool NetworkDeviceInfo::operator==(const NetworkDeviceInfo &other) const
|
||||
{
|
||||
return MacAddress(m_macAddress) == MacAddress(other.macAddress()) &&
|
||||
m_address == other.address() &&
|
||||
m_hostName == other.hostName() &&
|
||||
m_macAddressManufacturer == other.macAddressManufacturer() &&
|
||||
m_networkInterface.name() == other.networkInterface().name() &&
|
||||
isComplete() == other.isComplete();
|
||||
}
|
||||
|
||||
bool NetworkDeviceInfo::operator!=(const NetworkDeviceInfo &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const NetworkDeviceInfo &networkDeviceInfo)
|
||||
{
|
||||
dbg.nospace() << "NetworkDeviceInfo(" << networkDeviceInfo.address().toString();
|
||||
if (!networkDeviceInfo.hostName().isEmpty())
|
||||
dbg.nospace() << " (" << networkDeviceInfo.hostName() << ")";
|
||||
|
||||
dbg.nospace() << ", " << networkDeviceInfo.macAddress();
|
||||
if (!networkDeviceInfo.macAddress().isEmpty())
|
||||
dbg.nospace() << ", " << MacAddress(networkDeviceInfo.macAddress()).toString();
|
||||
|
||||
if (!networkDeviceInfo.macAddressManufacturer().isEmpty())
|
||||
dbg.nospace() << " (" << networkDeviceInfo.macAddressManufacturer() << ") ";
|
||||
|
||||
if (!networkDeviceInfo.hostName().isEmpty())
|
||||
dbg.nospace() << ", hostname: " << networkDeviceInfo.hostName();
|
||||
|
||||
if (networkDeviceInfo.networkInterface().isValid())
|
||||
dbg.nospace() << ", " << networkDeviceInfo.networkInterface().name();
|
||||
|
||||
dbg.nospace() << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -33,6 +33,7 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include <QDateTime>
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkInterface>
|
||||
|
||||
@ -43,7 +44,7 @@ class LIBNYMEA_EXPORT NetworkDeviceInfo
|
||||
public:
|
||||
explicit NetworkDeviceInfo();
|
||||
explicit NetworkDeviceInfo(const QString &macAddress);
|
||||
~NetworkDeviceInfo() = default;
|
||||
explicit NetworkDeviceInfo(const QHostAddress &address);
|
||||
|
||||
QString macAddress() const;
|
||||
void setMacAddress(const QString &macAddress);
|
||||
@ -61,6 +62,12 @@ public:
|
||||
void setNetworkInterface(const QNetworkInterface &networkInterface);
|
||||
|
||||
bool isValid() const;
|
||||
bool isComplete() const;
|
||||
|
||||
QString incompleteProperties() const;
|
||||
|
||||
bool operator==(const NetworkDeviceInfo &other) const;
|
||||
bool operator!=(const NetworkDeviceInfo &other) const;
|
||||
|
||||
private:
|
||||
QHostAddress m_address;
|
||||
@ -69,8 +76,14 @@ private:
|
||||
QString m_hostName;
|
||||
QNetworkInterface m_networkInterface;
|
||||
|
||||
bool m_macAddressSet = false;
|
||||
bool m_macAddressManufacturerSet = false;
|
||||
bool m_addressSet = false;
|
||||
bool m_hostNameSet = false;
|
||||
bool m_networkInterfaceSet = false;
|
||||
};
|
||||
|
||||
|
||||
QDebug operator<<(QDebug debug, const NetworkDeviceInfo &networkDeviceInfo);
|
||||
|
||||
|
||||
|
||||
@ -57,9 +57,14 @@ int NetworkDeviceInfos::indexFromHostAddress(const QHostAddress &address)
|
||||
}
|
||||
|
||||
int NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress)
|
||||
{
|
||||
return indexFromMacAddress(MacAddress(macAddress));
|
||||
}
|
||||
|
||||
int NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress)
|
||||
{
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (at(i).macAddress().toLower() == macAddress.toLower()) {
|
||||
if (MacAddress(at(i).macAddress()) == macAddress) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -77,7 +82,12 @@ bool NetworkDeviceInfos::hasMacAddress(const QString &macAddress)
|
||||
return indexFromMacAddress(macAddress) >= 0;
|
||||
}
|
||||
|
||||
NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address)
|
||||
bool NetworkDeviceInfos::hasMacAddress(const MacAddress &macAddress)
|
||||
{
|
||||
return indexFromMacAddress(macAddress) >= 0;
|
||||
}
|
||||
|
||||
NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address) const
|
||||
{
|
||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, *this) {
|
||||
if (networkDeviceInfo.address() == address) {
|
||||
@ -88,7 +98,7 @@ NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address)
|
||||
return NetworkDeviceInfo();
|
||||
}
|
||||
|
||||
NetworkDeviceInfo NetworkDeviceInfos::get(const QString &macAddress)
|
||||
NetworkDeviceInfo NetworkDeviceInfos::get(const QString &macAddress) const
|
||||
{
|
||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, *this) {
|
||||
if (networkDeviceInfo.macAddress() == macAddress) {
|
||||
@ -99,6 +109,25 @@ NetworkDeviceInfo NetworkDeviceInfos::get(const QString &macAddress)
|
||||
return NetworkDeviceInfo();
|
||||
}
|
||||
|
||||
NetworkDeviceInfo NetworkDeviceInfos::get(const MacAddress &macAddress) const
|
||||
{
|
||||
return get(macAddress.toString());
|
||||
}
|
||||
|
||||
void NetworkDeviceInfos::removeMacAddress(const QString &macAddress)
|
||||
{
|
||||
removeMacAddress(MacAddress(macAddress));
|
||||
}
|
||||
|
||||
void NetworkDeviceInfos::removeMacAddress(const MacAddress &macAddress)
|
||||
{
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (MacAddress(at(i).macAddress()) == macAddress) {
|
||||
remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkDeviceInfos::sortNetworkDevices()
|
||||
{
|
||||
std::sort(this->begin(), this->end(), [](const NetworkDeviceInfo& a, const NetworkDeviceInfo& b) {
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "libnymea.h"
|
||||
#include "macaddress.h"
|
||||
#include "networkdeviceinfo.h"
|
||||
|
||||
class LIBNYMEA_EXPORT NetworkDeviceInfos : public QVector<NetworkDeviceInfo>
|
||||
@ -45,12 +46,18 @@ public:
|
||||
|
||||
int indexFromHostAddress(const QHostAddress &address);
|
||||
int indexFromMacAddress(const QString &macAddress);
|
||||
int indexFromMacAddress(const MacAddress &macAddress);
|
||||
|
||||
bool hasHostAddress(const QHostAddress &address);
|
||||
bool hasMacAddress(const QString &macAddress);
|
||||
bool hasMacAddress(const MacAddress &macAddress);
|
||||
|
||||
NetworkDeviceInfo get(const QHostAddress &address);
|
||||
NetworkDeviceInfo get(const QString &macAddress);
|
||||
NetworkDeviceInfo get(const QHostAddress &address) const;
|
||||
NetworkDeviceInfo get(const QString &macAddress) const;
|
||||
NetworkDeviceInfo get(const MacAddress &macAddress) const;
|
||||
|
||||
void removeMacAddress(const QString &macAddress);
|
||||
void removeMacAddress(const MacAddress &macAddress);
|
||||
|
||||
void sortNetworkDevices();
|
||||
|
||||
|
||||
52
libnymea/network/networkdevicemonitor.cpp
Normal file
52
libnymea/network/networkdevicemonitor.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "networkdevicemonitor.h"
|
||||
#include "networkdeviceinfo.h"
|
||||
|
||||
NetworkDeviceMonitor::NetworkDeviceMonitor(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, NetworkDeviceMonitor *networkDeviceMonitor)
|
||||
{
|
||||
dbg.nospace() << "NetworkDeviceMonitor(" << networkDeviceMonitor->macAddress().toString();
|
||||
|
||||
if (!networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer().isEmpty())
|
||||
dbg.nospace() << " - " << networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer();
|
||||
|
||||
dbg.nospace() << ", " << networkDeviceMonitor->networkDeviceInfo().address().toString();
|
||||
|
||||
dbg.nospace() << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
65
libnymea/network/networkdevicemonitor.h
Normal file
65
libnymea/network/networkdevicemonitor.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef NETWORKDEVICEMONITOR_H
|
||||
#define NETWORKDEVICEMONITOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "libnymea.h"
|
||||
#include "macaddress.h"
|
||||
#include "networkdeviceinfo.h"
|
||||
|
||||
class LIBNYMEA_EXPORT NetworkDeviceMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NetworkDeviceMonitor(QObject *parent = nullptr);
|
||||
virtual ~NetworkDeviceMonitor() = default;
|
||||
|
||||
virtual MacAddress macAddress() const = 0;
|
||||
|
||||
virtual NetworkDeviceInfo networkDeviceInfo() const = 0;
|
||||
|
||||
virtual bool reachable() const = 0;
|
||||
virtual QDateTime lastSeen() const = 0;
|
||||
|
||||
signals:
|
||||
void reachableChanged(bool reachable);
|
||||
void lastSeenChanged(const QDateTime &lastSeen);
|
||||
void networkDeviceInfoChanged(const NetworkDeviceInfo &networkDeviceInfo);
|
||||
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, NetworkDeviceMonitor *networkDeviceMonitor);
|
||||
|
||||
#endif // NETWORKDEVICEMONITOR_H
|
||||
@ -13,7 +13,7 @@ QNetworkInterface NetworkUtils::getInterfaceForHostaddress(const QHostAddress &a
|
||||
if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol)
|
||||
continue;
|
||||
|
||||
if (address.isInSubnet(entry.ip(), entry.netmask().toIPv4Address())) {
|
||||
if (address.isInSubnet(entry.ip(), entry.prefixLength())) {
|
||||
return networkInterface;
|
||||
}
|
||||
}
|
||||
@ -32,3 +32,14 @@ QNetworkInterface NetworkUtils::getInterfaceForMacAddress(const QString &macAddr
|
||||
|
||||
return QNetworkInterface();
|
||||
}
|
||||
|
||||
QNetworkInterface NetworkUtils::getInterfaceForMacAddress(const MacAddress &macAddress)
|
||||
{
|
||||
foreach (const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
|
||||
if (MacAddress(networkInterface.hardwareAddress()) == macAddress) {
|
||||
return networkInterface;
|
||||
}
|
||||
}
|
||||
|
||||
return QNetworkInterface();
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkInterface>
|
||||
|
||||
#include "macaddress.h"
|
||||
|
||||
class NetworkUtils
|
||||
{
|
||||
public:
|
||||
@ -41,6 +43,7 @@ public:
|
||||
|
||||
static QNetworkInterface getInterfaceForHostaddress(const QHostAddress &address);
|
||||
static QNetworkInterface getInterfaceForMacAddress(const QString &macAddress);
|
||||
static QNetworkInterface getInterfaceForMacAddress(const MacAddress &macAddress);
|
||||
};
|
||||
|
||||
#endif // NETWORKUTILS_H
|
||||
|
||||
@ -49,6 +49,13 @@ NYMEA_LOGGING_CATEGORY(dcPingTraffic, "PingTraffic")
|
||||
|
||||
Ping::Ping(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_queueTimer = new QTimer(this);
|
||||
m_queueTimer->setInterval(20);
|
||||
m_queueTimer->setSingleShot(true);
|
||||
connect(m_queueTimer, &QTimer::timeout, this, [=](){
|
||||
sendNextReply();
|
||||
});
|
||||
|
||||
// Build socket descriptor
|
||||
m_socketDescriptor = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (m_socketDescriptor < 0) {
|
||||
@ -78,13 +85,6 @@ Ping::Ping(QObject *parent) : QObject(parent)
|
||||
m_socketNotifier = new QSocketNotifier(m_socketDescriptor, QSocketNotifier::Read, this);
|
||||
connect(m_socketNotifier, &QSocketNotifier::activated, this, &Ping::onSocketReadyRead);
|
||||
|
||||
m_queueTimer = new QTimer(this);
|
||||
m_queueTimer->setInterval(20);
|
||||
m_queueTimer->setSingleShot(true);
|
||||
connect(m_queueTimer, &QTimer::timeout, this, [=](){
|
||||
sendNextReply();
|
||||
});
|
||||
|
||||
m_socketNotifier->setEnabled(true);
|
||||
m_available = true;
|
||||
qCDebug(dcPing()) << "ICMP socket set up successfully (Socket ID:" << m_socketDescriptor << ")";
|
||||
@ -135,7 +135,7 @@ void Ping::sendNextReply()
|
||||
PingReply *reply = m_replyQueue.dequeue();
|
||||
//qCDebug(dcPing()) << "Send next reply," << m_replyQueue.count() << "left in queue";
|
||||
m_queueTimer->start();
|
||||
QTimer::singleShot(0, this, [=]() { performPing(reply); });
|
||||
QTimer::singleShot(0, reply, [=]() { performPing(reply); });
|
||||
}
|
||||
|
||||
void Ping::performPing(PingReply *reply)
|
||||
@ -146,6 +146,13 @@ void Ping::performPing(PingReply *reply)
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply->targetHostAddress().isNull()) {
|
||||
m_error = PingReply::ErrorInvalidHostAddress;
|
||||
qCWarning(dcPing()) << "Cannot send ping request" << m_error;
|
||||
finishReply(reply, m_error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get host ip address
|
||||
struct hostent *hostname = gethostbyname(reply->targetHostAddress().toString().toLocal8Bit().constData());
|
||||
struct sockaddr_in pingAddress;
|
||||
@ -411,7 +418,7 @@ void Ping::onHostLookupFinished(const QHostInfo &info)
|
||||
if (info.error() != QHostInfo::NoError) {
|
||||
qCWarning(dcPing()) << "Failed to look up hostname after successfull ping" << reply->targetHostAddress().toString() << info.error();
|
||||
} else {
|
||||
qCDebug(dcPing()) << "********Looked up hostname after successfull ping" << reply->targetHostAddress().toString() << info.hostName();
|
||||
qCDebug(dcPing()) << "Looked up hostname after successfull ping" << reply->targetHostAddress().toString() << info.hostName();
|
||||
if (info.hostName() != reply->targetHostAddress().toString()) {
|
||||
reply->m_hostName = info.hostName();
|
||||
}
|
||||
@ -419,4 +426,3 @@ void Ping::onHostLookupFinished(const QHostInfo &info)
|
||||
|
||||
finishReply(reply, PingReply::ErrorNoError);
|
||||
}
|
||||
|
||||
|
||||
@ -57,7 +57,8 @@ public:
|
||||
ErrorPermissionDenied,
|
||||
ErrorSocketError,
|
||||
ErrorTimeout,
|
||||
ErrorHostUnreachable
|
||||
ErrorHostUnreachable,
|
||||
ErrorInvalidHostAddress
|
||||
};
|
||||
Q_ENUM(Error)
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ SUBDIRS = \
|
||||
logging \
|
||||
loggingdirect \
|
||||
loggingloading \
|
||||
macaddress \
|
||||
mqttbroker \
|
||||
plugins \
|
||||
pythonplugins \
|
||||
@ -21,4 +22,3 @@ SUBDIRS = \
|
||||
webserver \
|
||||
websocketserver \
|
||||
#coap \ # temporary removed until fixed
|
||||
|
||||
|
||||
@ -1040,7 +1040,6 @@ void TestIntegrations::getEventTypes()
|
||||
|
||||
QVariantList eventTypes = response.toMap().value("params").toMap().value("eventTypes").toList();
|
||||
QCOMPARE(eventTypes.count(), resultCount);
|
||||
|
||||
}
|
||||
|
||||
void TestIntegrations::getStateTypes_data()
|
||||
|
||||
7
tests/auto/macaddress/macaddress.pro
Normal file
7
tests/auto/macaddress/macaddress.pro
Normal file
@ -0,0 +1,7 @@
|
||||
TARGET = testmacaddress
|
||||
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
SOURCES += testmacaddress.cpp
|
||||
|
||||
134
tests/auto/macaddress/testmacaddress.cpp
Normal file
134
tests/auto/macaddress/testmacaddress.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, GNU version 3. This project is distributed in the hope that it
|
||||
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "nymeatestbase.h"
|
||||
|
||||
#include <network/macaddress.h>
|
||||
|
||||
using namespace nymeaserver;
|
||||
|
||||
class TestMacAddress: public NymeaTestBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected slots:
|
||||
void initTestCase();
|
||||
|
||||
private:
|
||||
QString m_zeroMacString = QString("00:00:00:00:00:00");
|
||||
QByteArray m_zeroMacByteArray = QByteArray(6, '\0');
|
||||
|
||||
QString m_alphaNumericMacString = QString("01:23:45:ab:cd:ef");
|
||||
QByteArray m_alphaNumericByteArray = QByteArray::fromHex("012345abcdef");
|
||||
|
||||
private slots:
|
||||
void defaultConstructor();
|
||||
|
||||
void macAddressValidation_data();
|
||||
void macAddressValidation();
|
||||
|
||||
};
|
||||
|
||||
void TestMacAddress::initTestCase()
|
||||
{
|
||||
NymeaTestBase::initTestCase("*.debug=false\nTests.debug=true\n");
|
||||
}
|
||||
|
||||
void TestMacAddress::defaultConstructor()
|
||||
{
|
||||
MacAddress mac;
|
||||
QVERIFY(mac.isNull());
|
||||
QVERIFY(mac.isValid());
|
||||
QCOMPARE(mac.toByteArray().count(), 6);
|
||||
QCOMPARE(mac.toByteArray(), QByteArray(6, '\0'));
|
||||
QVERIFY(mac.toString() == m_zeroMacString);
|
||||
|
||||
QVERIFY(!MacAddress(QString("acme")).isValid());
|
||||
QVERIFY(MacAddress(QString("acme")).isNull());
|
||||
QVERIFY(!MacAddress(QByteArray()).isValid());
|
||||
QVERIFY(MacAddress(QByteArray()).isNull());
|
||||
|
||||
QVERIFY(MacAddress(QByteArray()).toByteArray().isEmpty());
|
||||
|
||||
QCOMPARE(MacAddress(m_zeroMacByteArray).toString(), m_zeroMacString);
|
||||
QCOMPARE(MacAddress(m_zeroMacString).toByteArray(), m_zeroMacByteArray);
|
||||
|
||||
QCOMPARE(MacAddress(m_alphaNumericByteArray).toString(), m_alphaNumericMacString);
|
||||
QCOMPARE(MacAddress(m_alphaNumericMacString).toByteArray(), m_alphaNumericByteArray);
|
||||
|
||||
QByteArray validRawData = QByteArray::fromHex("aabbccddeeff");
|
||||
QCOMPARE(MacAddress(QString("aa:bb:cc:dd:ee:ff")).toByteArray(), validRawData);
|
||||
QCOMPARE(MacAddress(QString("aa:bb:cc:dd:ee:ff")), MacAddress(validRawData));
|
||||
QCOMPARE(MacAddress(QString("aabbccddeeff")).toByteArray(), validRawData);
|
||||
QCOMPARE(MacAddress(QString("aabbccddeeff")), MacAddress(validRawData));
|
||||
}
|
||||
|
||||
void TestMacAddress::macAddressValidation_data()
|
||||
{
|
||||
QTest::addColumn<QString>("macString");
|
||||
QTest::addColumn<bool>("isValid");
|
||||
QTest::addColumn<bool>("isNull");
|
||||
QTest::addColumn<QString>("toString");
|
||||
|
||||
QString mixedString = "11:22:33:dd:ee:ff";
|
||||
QString aplhaString = "aa:bb:cc:dd:ee:ff";
|
||||
|
||||
QTest::newRow("Valid zero") << "00:00:00:00:00:00" << true << true << m_zeroMacString;
|
||||
QTest::newRow("Valid zero no colon") << "000000000000" << true << true << m_zeroMacString;
|
||||
QTest::newRow("Valid non zero lower") << "11:22:33:dd:ee:ff" << true << false << mixedString;
|
||||
QTest::newRow("Valid non zero upper") << "11:22:33:DD:EE:FF" << true << false << mixedString;
|
||||
QTest::newRow("Valid non zero mixed case") << "11:22:33:Dd:Ee:Ff" << true << false << mixedString;
|
||||
QTest::newRow("Valid non zero space separator") << "aa bb cc dd ee ff" << true << false << aplhaString;
|
||||
QTest::newRow("Valid non zero dash separator") << "aa-bb-cc-dd-ee-ff" << true << false << aplhaString;
|
||||
QTest::newRow("Valid non zero dot separator") << "aa.bb.cc.dd.ee.ff" << true << false << aplhaString;
|
||||
QTest::newRow("Valid non zero crazy separator") << "aa#bb?cclddxeeäff" << true << false << aplhaString;
|
||||
QTest::newRow("Valid non zero mixed separators") << "aa-bb cc.dd ee-ff" << true << false << aplhaString;
|
||||
QTest::newRow("Valid non zero without colon") << "aabbccddeeff" << true << false << aplhaString;
|
||||
QTest::newRow("Invalid characters") << "xx:yy:zz:dd:ee:ff" << false << true << m_zeroMacString;
|
||||
QTest::newRow("Too short") << "xx:yy:zz:dd:ee" << false << true << m_zeroMacString;
|
||||
QTest::newRow("Too long") << "xx:yy:zz:dd:ee:ee:ee" << false << true << m_zeroMacString;
|
||||
}
|
||||
|
||||
void TestMacAddress::macAddressValidation()
|
||||
{
|
||||
QFETCH(QString, macString);
|
||||
QFETCH(bool, isValid);
|
||||
QFETCH(bool, isNull);
|
||||
QFETCH(QString, toString);
|
||||
|
||||
MacAddress mac(macString);
|
||||
qCDebug(dcTests()) << "Verify" << macString << "resulting in" << mac;
|
||||
QCOMPARE(mac.isValid(), isValid);
|
||||
QCOMPARE(mac.isNull(), isNull);
|
||||
QCOMPARE(mac.toString(), toString);
|
||||
}
|
||||
|
||||
#include "testmacaddress.moc"
|
||||
QTEST_MAIN(TestMacAddress)
|
||||
Reference in New Issue
Block a user