Update network device info and make host address the primary idetifier instead of the MAC address

Introduce monitor mode for network device info
Update network device monitor
introduce networkdevice interface
This commit is contained in:
Simon Stürz 2024-12-02 16:18:23 +01:00
parent c46b86088a
commit 9b4b2d9b20
18 changed files with 791 additions and 388 deletions

View File

@ -38,6 +38,8 @@
#include <network/arpsocket.h>
#include <network/networkutils.h>
#define CACHE_VERSION 1
NYMEA_LOGGING_CATEGORY(dcNetworkDeviceDiscovery, "NetworkDeviceDiscovery")
namespace nymeaserver {
@ -128,7 +130,7 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "Starting internally a new discovery.";
m_currentDiscoveryReply = new NetworkDeviceDiscoveryReplyImpl(this);
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, this, &NetworkDeviceDiscoveryImpl::updateCache);
// connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, this, &NetworkDeviceDiscoveryImpl::updateCache);
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::finished, this, [this](){
// Finish all pending replies
foreach (NetworkDeviceDiscoveryReplyImpl *reply, m_pendingDiscoveryReplies) {
@ -137,10 +139,6 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
foreach (const NetworkDeviceInfo &info, m_currentDiscoveryReply->networkDeviceInfos()) {
reply->addCompleteNetworkDeviceInfo(info);
}
foreach (const NetworkDeviceInfo &info, m_currentDiscoveryReply->virtualNetworkDeviceInfos()) {
reply->addVirtualNetworkDeviceInfo(info);
}
}
// Delete the current reply before finishing the pending replies.
@ -159,8 +157,6 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
// Create the reply for the user
NetworkDeviceDiscoveryReplyImpl *reply = new NetworkDeviceDiscoveryReplyImpl(this);
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, reply, &NetworkDeviceDiscoveryReplyImpl::addCompleteNetworkDeviceInfo);
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered, reply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered);
m_pendingDiscoveryReplies.append(reply);
if (!available()) {
@ -171,19 +167,23 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
}
if (alreadyRunning) {
// Add already discovered network device infos in the next event loop
// so any connections after this method call will work as expected
// Emit allready discovered hosts so any integration can implement safly a connect on the host hostAddressDiscovered signal
// even if the internal discvoery was already running
QTimer::singleShot(0, reply, [this, reply](){
if (!m_currentDiscoveryReply)
return;
foreach (const NetworkDeviceInfo &networkDeviceInfo, m_currentDiscoveryReply->networkDeviceInfos()) {
reply->addCompleteNetworkDeviceInfo(networkDeviceInfo);
foreach (const QHostAddress &address, m_currentDiscoveryReply->currentCache().keys()) {
reply->hostAddressDiscovered(address);
}
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered, reply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered);
});
} else {
qCInfo(dcNetworkDeviceDiscovery()) << "Starting network device discovery ...";
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered, reply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered);
if (m_ping->available())
pingAllNetworkDevices();
@ -220,49 +220,52 @@ NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(const MacAddre
return nullptr;
}
// Make sure we create only one monitor per MAC and keep track how many user
// have access to this monitor otherwise an unregister could cause a crash in
// an other plugin plugin which might still need it
if (m_monitors.contains(macAddress)) {
m_monitorsReferenceCount[macAddress] += 1;
qCInfo(dcNetworkDeviceDiscovery()) << "Register network device monitor for" << macAddress << "which already exists. Returning existing monitor having now" << m_monitorsReferenceCount[macAddress] << "references.";
return m_monitors.value(macAddress);
}
// FIXME
return nullptr;
qCInfo(dcNetworkDeviceDiscovery()) << "Register new network device monitor for" << macAddress;
// // Make sure we create only one monitor per MAC and keep track how many user
// // have access to this monitor otherwise an unregister could cause a crash in
// // an other plugin plugin which might still need it
// if (m_monitors.contains(macAddress)) {
// m_monitorsReferenceCount[macAddress] += 1;
// qCInfo(dcNetworkDeviceDiscovery()) << "Register network device monitor for" << macAddress << "which already exists. Returning existing monitor having now" << m_monitorsReferenceCount[macAddress] << "references.";
// return m_monitors.value(macAddress);
// }
// Fill in cached information
NetworkDeviceInfo info;
if (m_networkInfoCache.contains(macAddress)) {
info = m_networkInfoCache.value(macAddress);
} else {
info.setMacAddress(macAddress.toString());
}
// qCInfo(dcNetworkDeviceDiscovery()) << "Register new network device monitor for" << macAddress;
NetworkDeviceMonitorImpl *monitor = new NetworkDeviceMonitorImpl(macAddress, this);
monitor->setNetworkDeviceInfo(info);
monitor->setLastSeen(m_lastSeen.value(macAddress, QDateTime()));
m_monitors.insert(macAddress, monitor);
m_monitorsReferenceCount[macAddress] = 1;
// // Fill in cached information
// NetworkDeviceInfo info;
// if (m_networkInfoCache.contains(macAddress)) {
// info = m_networkInfoCache.value(macAddress);
// } else {
// info.setMacAddress(macAddress.toString());
// }
if (!available()) {
qCWarning(dcNetworkDeviceDiscovery()) << "Registered monitor but the hardware resource is not available. The monitor will not work as expected" << monitor;
return monitor;
}
// NetworkDeviceMonitorImpl *monitor = new NetworkDeviceMonitorImpl(macAddress, this);
// monitor->setNetworkDeviceInfo(info);
// monitor->setLastSeen(m_lastSeen.value(macAddress, QDateTime()));
// m_monitors.insert(macAddress, monitor);
// m_monitorsReferenceCount[macAddress] = 1;
// Restart the monitor timer since we evaluate this one now
m_monitorTimer->start();
// if (!available()) {
// qCWarning(dcNetworkDeviceDiscovery()) << "Registered monitor but the hardware resource is not available. The monitor will not work as expected" << monitor;
// return monitor;
// }
if (!monitor->networkDeviceInfo().isValid()) {
qCDebug(dcNetworkDeviceDiscovery()) << "Adding network device monitor for unresolved mac address. Starting a discovery...";
NetworkDeviceDiscoveryReply *reply = discover();
connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
} else {
evaluateMonitor(monitor);
}
// // Restart the monitor timer since we evaluate this one now
// m_monitorTimer->start();
qCDebug(dcNetworkDeviceDiscovery()) << "Registered successfully" << monitor;
return monitor;
// if (!monitor->networkDeviceInfo().isValid()) {
// qCDebug(dcNetworkDeviceDiscovery()) << "Adding network device monitor for unresolved mac address. Starting a discovery...";
// NetworkDeviceDiscoveryReply *reply = discover();
// connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
// } else {
// evaluateMonitor(monitor);
// }
// qCDebug(dcNetworkDeviceDiscovery()) << "Registered successfully" << monitor;
// return monitor;
}
void NetworkDeviceDiscoveryImpl::unregisterMonitor(const MacAddress &macAddress)
@ -291,7 +294,8 @@ void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *network
if (!m_monitors.values().contains(qobject_cast<NetworkDeviceMonitorImpl *>(networkDeviceMonitor)))
return;
unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress()));
// FIXME
//unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress()));
}
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries)
@ -328,7 +332,7 @@ bool NetworkDeviceDiscoveryImpl::sendArpRequest(const QHostAddress &address)
return false;
}
QHash<MacAddress, NetworkDeviceInfo> NetworkDeviceDiscoveryImpl::cache() const
NetworkDeviceInfos NetworkDeviceDiscoveryImpl::cache() const
{
return m_networkInfoCache;
}
@ -461,18 +465,20 @@ void NetworkDeviceDiscoveryImpl::watchPingReply(PingReply *reply)
{
connect(reply, &PingReply::finished, this, [=](){
// Search cache for mac address and update last seen
if (reply->error() == PingReply::ErrorNoError) {
foreach (const NetworkDeviceInfo &info, m_networkInfoCache) {
if (info.address() == reply->targetHostAddress()) {
// Found info for this ip, update the cache
MacAddress macAddress(info.macAddress());
if (!macAddress.isNull() && m_networkInfoCache.contains(macAddress)) {
m_lastSeen[macAddress] = QDateTime::currentDateTime();
saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
}
}
}
}
// FIXME
// if (reply->error() == PingReply::ErrorNoError) {
// foreach (const NetworkDeviceInfo &info, m_networkInfoCache) {
// if (info.address() == reply->targetHostAddress()) {
// // Found info for this ip, update the cache
// MacAddress macAddress(info.macAddress());
// if (!macAddress.isNull() && m_networkInfoCache.contains(macAddress)) {
// m_lastSeen[macAddress] = QDateTime::currentDateTime();
// saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
// }
// }
// }
// }
// Update any monitor
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.values()) {
@ -490,39 +496,54 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
m_networkInfoCache.clear();
QDateTime now = QDateTime::currentDateTime();
m_cacheSettings->beginGroup("NetworkDeviceInfos");
foreach (const QString &macAddress, m_cacheSettings->childGroups()) {
m_cacheSettings->beginGroup(macAddress);
uint cacheVersion = m_cacheSettings->value("version", 0).toUInt();
MacAddress mac(macAddress);
QDateTime lastSeen = QDateTime::fromMSecsSinceEpoch(m_cacheSettings->value("lastSeen").toLongLong());
// Load only the cache if we have the same format, otherwise we discard the cache and start over with the current version
// No need to migrate the caches, a new discovery will to that for us ...
if (cacheVersion == CACHE_VERSION) {
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("");
// m_cacheSettings->endGroup(); // mac address
// 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("");
// }
// 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("");
m_cacheSettings->endGroup(); // mac address
continue;
}
m_cacheSettings->endGroup(); // NetworkDeviceInfos
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()));
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "The cache format version has changed. Discard the network device cache ...";
m_cacheSettings->setValue("version", CACHE_VERSION);
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->beginGroup("NetworkDeviceInfos");
m_cacheSettings->remove("");
m_cacheSettings->endGroup(); // NetworkDeviceInfos
}
m_cacheSettings->endGroup(); // NetworkDeviceInfos
qCInfo(dcNetworkDeviceDiscovery()) << "Loaded" << m_networkInfoCache.count() << "network device infos from cache.";
@ -535,14 +556,14 @@ void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const 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
m_cacheSettings->sync();
// m_networkInfoCache.remove(macAddress);
// m_lastSeen.remove(macAddress);
// m_cacheSettings->beginGroup("NetworkDeviceInfos");
// m_cacheSettings->beginGroup(macAddress.toString());
// m_cacheSettings->remove("");
// m_cacheSettings->endGroup(); // mac address
// m_cacheSettings->endGroup(); // NetworkDeviceInfos
// m_cacheSettings->sync();
}
void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo)
@ -550,33 +571,43 @@ void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo
if (!deviceInfo.isValid() || !deviceInfo.isComplete())
return;
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
m_cacheSettings->sync();
// 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
// m_cacheSettings->sync();
}
void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo)
{
MacAddress macAddress(deviceInfo.macAddress());
if (macAddress.isNull())
// MacAddress macAddress(deviceInfo.macAddress());
// if (macAddress.isNull())
// return;
// FIXME
// if (m_monitors.contains(macAddress)) {
// NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
// monitor->setNetworkDeviceInfo(deviceInfo);
// }
// Save only if changed
int index = m_networkInfoCache.indexFromHostAddress(deviceInfo.address());
if (index >= 0 && m_networkInfoCache.at(index) == deviceInfo)
return;
if (m_monitors.contains(macAddress)) {
NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
monitor->setNetworkDeviceInfo(deviceInfo);
if (index < 0) {
m_networkInfoCache.append(deviceInfo);
} else {
m_networkInfoCache[index] = deviceInfo;
}
if (m_networkInfoCache.value(macAddress) == deviceInfo)
return;
m_networkInfoCache[macAddress] = deviceInfo;
saveNetworkDeviceCache(deviceInfo);
}
@ -644,29 +675,31 @@ void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &inte
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));
}
}
// FIXME
// if (m_networkInfoCache.hasMacAddress(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);
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());
}
// NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
// if (monitor) {
// monitor->setLastSeen(now);
// 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());
// }
if (monitor->networkDeviceInfo().isComplete()) {
monitor->setReachable(true);
}
}
// if (monitor->networkDeviceInfo().isComplete()) {
// monitor->setReachable(true);
// }
// }
// Check if we have currently reply running
if (!m_currentDiscoveryReply)
@ -677,8 +710,8 @@ void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &inte
// Check if we know the mac address manufacturer from the cache
bool requiresMacAddressLookup = true;
if (m_networkInfoCache.contains(macAddress)) {
QString cachedManufacturer = m_networkInfoCache[macAddress].macAddressManufacturer();
if (m_macVendorCache.contains(macAddress)) {
QString cachedManufacturer = m_macVendorCache.value(macAddress);
if (!cachedManufacturer.isEmpty()) {
// Found the mac address manufacturer in the cache, let's use that one...
qCDebug(dcNetworkDeviceDiscovery()) << "Using cached manufacturer " << cachedManufacturer << "for" << macAddress.toString();
@ -700,6 +733,9 @@ void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &inte
// Note: set the mac manufacturer explicitly to make the info complete (even an empty sring)
qCDebug(dcNetworkDeviceDiscovery()) << "MAC manufacturer lookup finished for" << macAddress << ":" << reply->manufacturer();
if (!reply->manufacturer().isEmpty())
m_macVendorCache.insert(macAddress, reply->manufacturer());
if (m_currentDiscoveryReply) {
m_currentDiscoveryReply->processMacManufacturer(macAddress, reply->manufacturer());

View File

@ -79,7 +79,7 @@ public:
bool sendArpRequest(const QHostAddress &address) override;
QHash<MacAddress, NetworkDeviceInfo> cache() const override;
NetworkDeviceInfos cache() const override;
protected:
void setEnabled(bool enabled) override;
@ -116,8 +116,12 @@ private:
QHash<MacAddress, int> m_monitorsReferenceCount;
QHash<MacAddress, QDateTime> m_lastSeen;
QHash<MacAddress, QString> m_macVendorCache;
QHash<QHostAddress, NetworkDeviceInfo> m_infos;
QSettings *m_cacheSettings;
QHash<MacAddress, NetworkDeviceInfo> m_networkInfoCache;
NetworkDeviceInfos m_networkInfoCache;
void pingAllNetworkDevices();

View File

@ -29,6 +29,7 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "networkdevicediscoveryreplyimpl.h"
#include "network/networkutils.h"
#include "loggingcategories.h"
#include <QDateTime>
@ -48,11 +49,6 @@ NetworkDeviceInfos NetworkDeviceDiscoveryReplyImpl::networkDeviceInfos() const
return m_networkDeviceInfos;
}
NetworkDeviceInfos NetworkDeviceDiscoveryReplyImpl::virtualNetworkDeviceInfos() const
{
return m_virtualNetworkDeviceInfos;
}
bool NetworkDeviceDiscoveryReplyImpl::isFinished() const
{
return m_isFinished;
@ -65,49 +61,40 @@ void NetworkDeviceDiscoveryReplyImpl::setFinished(bool finished)
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;
}
if (m_networkDeviceCache.contains(address)) {
// Update existing hostname...
m_networkDeviceCache[address].setHostName(hostName);
evaluateMonitorMode(address);
} else {
// Adding new host...
NetworkDeviceInfo info;
info.setAddress(address);
info.setHostName(hostName);
m_networkDeviceCache.insert(address, info);
evaluateMonitorMode(address);
// First time seeing this host address
emit hostAddressDiscovered(address);
}
// 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);
// First time seeing this host address
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);
if (m_networkDeviceCache.contains(address)) {
m_networkDeviceCache[address].addMacAddress(macAddress);
m_networkDeviceCache[address].setNetworkInterface(interface);
} else {
NetworkDeviceInfo info(macAddress.toString());
info.setAddress(address);
info.setNetworkInterface(interface);
info.setMacAddress(macAddress.toString());
m_networkDeviceCache[macAddress] = info;
} 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;
// First time seeing this host address
emit hostAddressDiscovered(address);
}
m_networkDeviceCache[address] = info;
// First time seeing this host address
emit hostAddressDiscovered(address);
}
verifyComplete(macAddress);
evaluateMonitorMode(address);
}
void NetworkDeviceDiscoveryReplyImpl::processMacManufacturer(const MacAddress &macAddress, const QString &manufacturer)
@ -115,43 +102,35 @@ void NetworkDeviceDiscoveryReplyImpl::processMacManufacturer(const MacAddress &m
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;
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
if (info.macAddressInfos().hasMacAddress(macAddress)) {
m_networkDeviceCache[info.address()].addMacAddress(macAddress, manufacturer);
evaluateMonitorMode(info.address());
}
}
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;
// Add the discovery cache to the final result
foreach (const QHostAddress &address, m_networkDeviceCache.keys()) {
NetworkDeviceInfo info = m_networkDeviceCache.value(macAddress);
MacAddress infoMacAddress(info.macAddress());
if (m_networkDeviceCache.value(address).macAddressInfos().isEmpty() && !m_networkDeviceCache.value(address).networkInterface().isValid()) {
// Set the network interface for the virtual hosts like VPN where we are not receiving any ARP information
m_networkDeviceCache[address].setNetworkInterface(NetworkUtils::getInterfaceForHostaddress(address));
}
NetworkDeviceInfo info = m_networkDeviceCache.value(address);
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);
}
qCDebug(dcNetworkDeviceDiscovery()) << "Adding incomplete" << info << "to the final result:" << info.incompleteProperties();
m_networkDeviceCache[address].forceComplete();
evaluateMonitorMode(address);
m_networkDeviceInfos.append(m_networkDeviceCache.value(address));
}
// Done, lets sort the result and inform
@ -165,87 +144,69 @@ void NetworkDeviceDiscoveryReplyImpl::processDiscoveryFinished()
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;
}
foreach (const MacAddress &macAddress, m_networkDeviceCache.keys()) {
if (m_networkDeviceInfos.hasMacAddress(macAddress))
continue;
NetworkDeviceInfo info = m_networkDeviceCache.value(macAddress);
qCDebug(dcNetworkDeviceDiscovery()) << "Unhandled information:" << info << "Valid:" << info.isValid() << "Complete:" << info.isComplete() << info.incompleteProperties();
}
m_isFinished = true;
emit finished();
}
QHash<QHostAddress, NetworkDeviceInfo> NetworkDeviceDiscoveryReplyImpl::currentCache() const
{
return m_networkDeviceCache;
}
void NetworkDeviceDiscoveryReplyImpl::addCompleteNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo)
{
// Note: this method will be called only from the discovery
if (!m_networkDeviceInfos.hasMacAddress(networkDeviceInfo.macAddress())) {
if (!m_networkDeviceInfos.hasHostAddress(networkDeviceInfo.address()))
m_networkDeviceInfos.append(networkDeviceInfo);
emit hostAddressDiscovered(networkDeviceInfo.address());
emit networkDeviceInfoAdded(networkDeviceInfo);
}
}
void NetworkDeviceDiscoveryReplyImpl::addVirtualNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo)
void NetworkDeviceDiscoveryReplyImpl::evaluateMonitorMode(const QHostAddress &address)
{
// Note: this method will be called only from the discovery
if (!m_networkDeviceInfos.hasHostAddress(networkDeviceInfo.address())) {
m_virtualNetworkDeviceInfos.append(networkDeviceInfo);
}
}
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: Evaluating monitor mode for host" << address.toString();
QString NetworkDeviceDiscoveryReplyImpl::macAddressFromHostAddress(const QHostAddress &address)
{
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
if (info.address() == address) {
return info.macAddress();
if (m_networkDeviceCache.value(address).macAddressInfos().isEmpty()) {
// Not discovered yet, or this is a virtual host like VPN
if (m_networkDeviceCache.value(address).hostName().isEmpty()) {
m_networkDeviceCache[address].setMonitorMode(NetworkDeviceInfo::MonitorModeIp);
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: No MAC address and no hostname, using IP only";
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: No MAC address, but we have a hostname.";
m_networkDeviceCache[address].setMonitorMode(NetworkDeviceInfo::MonitorModeHostname);
}
}
return QString();
}
} else {
// We have at least one mac address, check if there are other network devices with this MAC or if we have multiple MAC addresses
if (m_networkDeviceCache.value(address).macAddressInfos().count() == 1) {
bool NetworkDeviceDiscoveryReplyImpl::hasHostAddress(const QHostAddress &address)
{
return ! macAddressFromHostAddress(address).isEmpty();
}
bool uniqueMac = true;
// Check if this mac is unique
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
if (info.address() == address)
continue;
void NetworkDeviceDiscoveryReplyImpl::verifyComplete(const MacAddress &macAddress)
{
if (!m_networkDeviceCache.contains(macAddress))
return;
if (info.macAddressInfos().hasMacAddress(m_networkDeviceCache.value(address).macAddressInfos().first().macAddress())) {
uniqueMac = false;
break;
}
}
if (m_networkDeviceCache[macAddress].isComplete() && m_networkDeviceCache[macAddress].isValid()) {
if (m_networkDeviceInfos.hasMacAddress(macAddress)) {
if (m_networkDeviceInfos.get(macAddress) != m_networkDeviceCache.value(macAddress)) {
qCDebug(dcNetworkDeviceDiscovery()) << "One MAC address seem to be reachable using 2 IP addresses, which is OK. Not updating the network device info and keeping the current information.";
qCDebug(dcNetworkDeviceDiscovery()) << "--> Keeping " << m_networkDeviceInfos.get(macAddress);
qCDebug(dcNetworkDeviceDiscovery()) << "--> Ignoring" << m_networkDeviceCache.value(macAddress);
if (!uniqueMac) {
if (m_networkDeviceCache.value(address).hostName().isEmpty()) {
m_networkDeviceCache[address].setMonitorMode(NetworkDeviceInfo::MonitorModeIp);
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: The MAC address of" << address.toString() << "is not unique in this network and no hostname available, using IP only";
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: The MAC address of" << address.toString() << "is not unique in this network but we have a hostname";
m_networkDeviceCache[address].setMonitorMode(NetworkDeviceInfo::MonitorModeHostname);
}
}
} else {
m_networkDeviceInfos.append(m_networkDeviceCache.value(macAddress));
emit networkDeviceInfoAdded(m_networkDeviceCache[macAddress]);
// Multiple MAC addresses
if (m_networkDeviceCache.value(address).hostName().isEmpty()) {
m_networkDeviceCache[address].setMonitorMode(NetworkDeviceInfo::MonitorModeIp);
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: Multiple MAC addresses and no hostname, using IP only";
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: Multiple MAC addresses, but we have a hostname.";
m_networkDeviceCache[address].setMonitorMode(NetworkDeviceInfo::MonitorModeHostname);
}
}
}
}

View File

@ -48,7 +48,6 @@ public:
~NetworkDeviceDiscoveryReplyImpl() override = default;
NetworkDeviceInfos networkDeviceInfos() const override;
NetworkDeviceInfos virtualNetworkDeviceInfos() const override;
bool isFinished() const override;
void setFinished(bool finished);
@ -60,28 +59,19 @@ public:
void processDiscoveryFinished();
QHash<QHostAddress, NetworkDeviceInfo> currentCache() const;
public slots:
void addCompleteNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
void addVirtualNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
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;
QHash<QHostAddress, NetworkDeviceInfo> m_networkDeviceCache;
qint64 m_startTimestamp;
bool m_isFinished = false;
// 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);
NetworkDeviceInfos m_networkDeviceInfos;
void evaluateMonitorMode(const QHostAddress &address);
};
}

View File

@ -62,7 +62,7 @@ public slots:
void addThingDescriptor(const ThingDescriptor &thingDescriptor);
void addThingDescriptors(const ThingDescriptors &thingDescriptors);
void finish(Thing::ThingError status, const QString &displayMessage = QString());
void finish(Thing::ThingError status, const QString &displayMessage = QString());
signals:
void finished();

View File

@ -57,6 +57,8 @@ HEADERS += \
network/arpsocket.h \
network/macaddress.h \
network/macaddressdatabasereply.h \
network/macaddressinfo.h \
network/macaddressinfos.h \
network/networkdevicediscovery.h \
network/networkdevicediscoveryreply.h \
network/networkdeviceinfo.h \
@ -175,6 +177,8 @@ SOURCES += \
network/arpsocket.cpp \
network/macaddress.cpp \
network/macaddressdatabasereply.cpp \
network/macaddressinfo.cpp \
network/macaddressinfos.cpp \
network/networkdevicediscovery.cpp \
network/networkdeviceinfo.cpp \
network/networkdeviceinfos.cpp \

View File

@ -0,0 +1,103 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 "macaddressinfo.h"
#include <QDebug>
MacAddressInfo::MacAddressInfo()
{
}
MacAddressInfo::MacAddressInfo(const MacAddress &macAddress)
: m_macAddress{macAddress}
{
}
MacAddressInfo::MacAddressInfo(const MacAddress &macAddress, const QString &vendorName)
: m_macAddress{macAddress},
m_vendorName{vendorName},
m_vendorNameSet{true}
{
}
MacAddress MacAddressInfo::macAddress() const
{
return m_macAddress;
}
QString MacAddressInfo::vendorName() const
{
return m_vendorName;
}
void MacAddressInfo::setVendorName(const QString &vendorName)
{
m_vendorName = vendorName;
m_vendorNameSet = true;
}
bool MacAddressInfo::isValid() const
{
return !m_macAddress.isNull();
}
bool MacAddressInfo::isComplete() const
{
return isValid() && m_vendorNameSet;
}
bool MacAddressInfo::operator==(const MacAddressInfo &other) const
{
return m_macAddress == other.macAddress() &&
m_vendorName == other.vendorName() &&
isComplete() == other.isComplete();
}
bool MacAddressInfo::operator!=(const MacAddressInfo &other) const
{
return !operator==(other);
}
QDebug operator<<(QDebug debug, const MacAddressInfo &addressInfo)
{
QDebugStateSaver saver(debug);
debug.nospace() << addressInfo.macAddress().toString() << " (";
if (addressInfo.vendorName().isEmpty()) {
debug.nospace() << "unknown";
} else {
debug.nospace() << addressInfo.vendorName();
}
debug.nospace() << ")";
return debug;
}

View File

@ -0,0 +1,64 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 MACADDRESSINFO_H
#define MACADDRESSINFO_H
#include "macaddress.h"
class MacAddressInfo
{
public:
explicit MacAddressInfo();
explicit MacAddressInfo(const MacAddress &macAddress);
explicit MacAddressInfo(const MacAddress &macAddress, const QString &vendorName);
MacAddress macAddress() const;
QString vendorName() const;
void setVendorName(const QString &vendorName);
bool isValid() const;
bool isComplete() const;
bool operator==(const MacAddressInfo &other) const;
bool operator!=(const MacAddressInfo &other) const;
private:
MacAddress m_macAddress;
QString m_vendorName;
bool m_vendorNameSet = false;
};
QDebug operator<<(QDebug debug, const MacAddressInfo &addressInfo);
#endif // MACADDRESSINFO_H

View File

@ -0,0 +1,118 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 "macaddressinfos.h"
MacAddressInfos::MacAddressInfos()
{
}
MacAddressInfos::MacAddressInfos(const QVector<MacAddressInfo> &other)
: QVector<MacAddressInfo>(other)
{
}
int MacAddressInfos::indexFromMacAddress(const QString &macAddress)
{
return indexFromMacAddress(MacAddress(macAddress));
}
int MacAddressInfos::indexFromMacAddress(const MacAddress &macAddress)
{
for (int i = 0; i < size(); i++) {
if (MacAddress(at(i).macAddress()) == macAddress) {
return i;
}
}
return -1;
}
bool MacAddressInfos::hasMacAddress(const QString &macAddress)
{
return indexFromMacAddress(macAddress) >= 0;
}
bool MacAddressInfos::hasMacAddress(const MacAddress &macAddress)
{
return indexFromMacAddress(macAddress) >= 0;
}
MacAddressInfo MacAddressInfos::get(const QString &macAddress) const
{
return get(MacAddress(macAddress));
}
MacAddressInfo MacAddressInfos::get(const MacAddress &macAddress) const
{
foreach (const MacAddressInfo &info, *this) {
if (info.macAddress() == macAddress) {
return info;
}
}
return MacAddressInfo();
}
void MacAddressInfos::removeMacAddress(const QString &macAddress)
{
removeMacAddress(MacAddress(macAddress));
}
void MacAddressInfos::removeMacAddress(const MacAddress &macAddress)
{
for (int i = 0; i < size(); i++) {
if (MacAddress(at(i).macAddress()) == macAddress) {
remove(i);
}
}
}
void MacAddressInfos::sortInfos()
{
std::sort(this->begin(), this->end(), [](const MacAddressInfo& a, const MacAddressInfo& b) {
return a.macAddress().toByteArray() < b.macAddress().toByteArray();
});
}
bool MacAddressInfos::isComplete() const
{
bool complete = true;
foreach (const MacAddressInfo &info, *this) {
if (!info.isComplete()) {
complete = false;
break;
}
}
return complete;
}

View File

@ -0,0 +1,60 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 MACADDRESSINFOS_H
#define MACADDRESSINFOS_H
#include <QVector>
#include "macaddressinfo.h"
class MacAddressInfos : public QVector<MacAddressInfo>
{
public:
explicit MacAddressInfos();
MacAddressInfos(const QVector<MacAddressInfo> &other);
int indexFromMacAddress(const QString &macAddress);
int indexFromMacAddress(const MacAddress &macAddress);
bool hasMacAddress(const QString &macAddress);
bool hasMacAddress(const MacAddress &macAddress);
MacAddressInfo get(const QString &macAddress) const;
MacAddressInfo get(const MacAddress &macAddress) const;
void removeMacAddress(const QString &macAddress);
void removeMacAddress(const MacAddress &macAddress);
void sortInfos();
bool isComplete() const;
};
#endif // MACADDRESSINFOS_H

View File

@ -67,7 +67,7 @@ public:
virtual bool sendArpRequest(const QHostAddress &address) = 0;
virtual QHash<MacAddress, NetworkDeviceInfo> cache() const = 0;
virtual NetworkDeviceInfos cache() const = 0;
signals:
void runningChanged(bool running);

View File

@ -44,17 +44,11 @@ public:
virtual ~NetworkDeviceDiscoveryReply() = default;
virtual NetworkDeviceInfos networkDeviceInfos() const = 0;
virtual NetworkDeviceInfos virtualNetworkDeviceInfos() const = 0;
virtual bool isFinished() const = 0;
signals:
// Emitted whenever a certain host address has been pinged successfully
void hostAddressDiscovered(const QHostAddress &address);
// Emited whenerver a valid NetworkDeviceInfo has been added
void networkDeviceInfoAdded(const NetworkDeviceInfo &networkDeviceInfo);
void finished();
};

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -36,38 +36,16 @@ NetworkDeviceInfo::NetworkDeviceInfo()
}
NetworkDeviceInfo::NetworkDeviceInfo(const QString &macAddress):
m_macAddress(macAddress)
NetworkDeviceInfo::NetworkDeviceInfo(const QString &macAddress)
{
m_macAddressSet = true;
addMacAddress(MacAddress(macAddress));
}
NetworkDeviceInfo::NetworkDeviceInfo(const QHostAddress &address):
m_address(address)
m_address{address},
m_addressSet{true}
{
m_addressSet = true;
}
QString NetworkDeviceInfo::macAddress() const
{
return m_macAddress;
}
void NetworkDeviceInfo::setMacAddress(const QString &macAddress)
{
m_macAddress = macAddress;
m_macAddressSet = true;
}
QString NetworkDeviceInfo::macAddressManufacturer() const
{
return m_macAddressManufacturer;
}
void NetworkDeviceInfo::setMacAddressManufacturer(const QString &macAddressManufacturer)
{
m_macAddressManufacturer = macAddressManufacturer;
m_macAddressManufacturerSet = true;
}
QHostAddress NetworkDeviceInfo::address() const
@ -92,6 +70,33 @@ void NetworkDeviceInfo::setHostName(const QString &hostName)
m_hostNameSet = true;
}
MacAddressInfos NetworkDeviceInfo::macAddressInfos() const
{
return m_macAddressInfos;
}
void NetworkDeviceInfo::addMacAddress(const MacAddress &macAddress)
{
if (m_macAddressInfos.hasMacAddress(macAddress))
return;
m_macAddressInfos.append(MacAddressInfo(macAddress));
// Note: we have to sort them in order to compare MacAddressInfos
m_macAddressInfos.sortInfos();
}
void NetworkDeviceInfo::addMacAddress(const MacAddress &macAddress, const QString &vendorName)
{
int index = m_macAddressInfos.indexFromMacAddress(macAddress);
if (index >= 0) {
m_macAddressInfos[index].setVendorName(vendorName);
} else {
m_macAddressInfos.append(MacAddressInfo(macAddress, vendorName));
// Note: we have to sort them in order to compare MacAddressInfos
m_macAddressInfos.sortInfos();
}
}
QNetworkInterface NetworkDeviceInfo::networkInterface() const
{
return m_networkInterface;
@ -103,34 +108,60 @@ void NetworkDeviceInfo::setNetworkInterface(const QNetworkInterface &networkInte
m_networkInterfaceSet = true;
}
NetworkDeviceInfo::MonitorMode NetworkDeviceInfo::monitorMode() const
{
return m_monitorMode;
}
void NetworkDeviceInfo::setMonitorMode(MonitorMode monitorMode)
{
m_monitorMode = monitorMode;
}
bool NetworkDeviceInfo::isValid() const
{
return (!m_address.isNull() || !MacAddress(m_macAddress).isNull()) && m_networkInterface.isValid();
return (!m_address.isNull() || m_macAddressInfos.isEmpty()) && m_networkInterface.isValid();
}
bool NetworkDeviceInfo::isComplete() const
{
return m_macAddressSet && m_macAddressManufacturerSet && m_addressSet && m_hostNameSet && m_networkInterfaceSet;
if (m_forceComplete)
return true;
return !m_macAddressInfos.isEmpty() && m_macAddressInfos.isComplete() && m_addressSet && m_hostNameSet && m_networkInterfaceSet;
}
void NetworkDeviceInfo::forceComplete()
{
m_forceComplete = true;
}
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");
if (m_macAddressInfos.isEmpty())
list.append("MAC address not set");
if (!m_macAddressInfos.isEmpty() && !m_macAddressInfos.isComplete())
list.append("MAC infos incomplete");
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();
return m_address == other.address() &&
m_macAddressInfos == other.macAddressInfos() &&
m_hostName == other.hostName() &&
m_networkInterface.name() == other.networkInterface().name() &&
m_monitorMode == other.monitorMode() &&
isComplete() == other.isComplete();
}
bool NetworkDeviceInfo::operator!=(const NetworkDeviceInfo &other) const
@ -141,20 +172,30 @@ bool NetworkDeviceInfo::operator!=(const NetworkDeviceInfo &other) const
QDebug operator<<(QDebug dbg, const NetworkDeviceInfo &networkDeviceInfo)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "NetworkDeviceInfo(" << networkDeviceInfo.address().toString();
dbg.nospace().noquote() << "NetworkDeviceInfo(" << networkDeviceInfo.address().toString();
if (!networkDeviceInfo.macAddress().isEmpty())
dbg.nospace() << ", " << MacAddress(networkDeviceInfo.macAddress()).toString();
dbg.nospace().noquote() << ", Monitor mode: ";
switch (networkDeviceInfo.monitorMode()) {
case NetworkDeviceInfo::MonitorModeMac:
dbg.nospace().noquote() << "MAC";
break;
case NetworkDeviceInfo::MonitorModeHostname:
dbg.nospace().noquote() << "hostname";
break;
case NetworkDeviceInfo::MonitorModeIp:
dbg.nospace().noquote() << "IP";
break;
}
if (!networkDeviceInfo.macAddressManufacturer().isEmpty())
dbg.nospace() << " (" << networkDeviceInfo.macAddressManufacturer() << ") ";
foreach (const MacAddressInfo &macInfo, networkDeviceInfo.macAddressInfos())
dbg.nospace().noquote() << ", " << macInfo;
if (!networkDeviceInfo.hostName().isEmpty())
dbg.nospace() << ", hostname: " << networkDeviceInfo.hostName();
dbg.nospace().noquote() << ", hostname: " << networkDeviceInfo.hostName();
if (networkDeviceInfo.networkInterface().isValid())
dbg.nospace() << ", " << networkDeviceInfo.networkInterface().name();
dbg.nospace().noquote() << ", " << networkDeviceInfo.networkInterface().name();
dbg.nospace() << ")";
dbg.nospace().noquote() << ")";
return dbg;
}

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Copyright 2013 - 2024, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -38,32 +38,52 @@
#include <QNetworkInterface>
#include "libnymea.h"
#include "macaddressinfos.h"
class LIBNYMEA_EXPORT NetworkDeviceInfo
{
Q_GADGET
public:
// Virtual hosts are devices with no MAC address available or not unique MAC address and the MAC address can not be used for the NetworkDeviceMonitor.
// Examples for virtual hosts are
// - VPN network hosts (no MAC address)
// - Webservers outside the network (domains)
// - Devices behind older wifi repeaters, multiple hosts (individual devices) have the same virtual MAC address
// - Hosts which are accessable over multiple interfaces within the same network (i.e. WLAN + LAN),
// they can be reached using both MAC addresses and both IP addresses (linux feature)
enum MonitorMode {
MonitorModeMac = 0x01, // Unique MAC address within the network
MonitorModeHostname = 0x02, // DNS hostname available, but no MAC address or not unique MAC available
MonitorModeIp = 0x03 // Only the IP can be used to monitor, simple ping on reachable
};
Q_ENUM(MonitorMode)
explicit NetworkDeviceInfo();
explicit NetworkDeviceInfo(const QString &macAddress);
explicit NetworkDeviceInfo(const QHostAddress &address);
QString macAddress() const;
void setMacAddress(const QString &macAddress);
QString macAddressManufacturer() const;
void setMacAddressManufacturer(const QString &macAddressManufacturer);
QHostAddress address() const;
void setAddress(const QHostAddress &address);
QString hostName() const;
void setHostName(const QString &hostName);
MacAddressInfos macAddressInfos() const;
void addMacAddress(const MacAddress &macAddress);
void addMacAddress(const MacAddress &macAddress, const QString &vendorName);
QNetworkInterface networkInterface() const;
void setNetworkInterface(const QNetworkInterface &networkInterface);
MonitorMode monitorMode() const;
void setMonitorMode(MonitorMode monitorMode);
bool isValid() const;
bool isComplete() const;
void forceComplete();
QString incompleteProperties() const;
bool operator==(const NetworkDeviceInfo &other) const;
@ -71,20 +91,17 @@ public:
private:
QHostAddress m_address;
QString m_macAddress;
QString m_macAddressManufacturer;
MacAddressInfos m_macAddressInfos;
QString m_hostName;
QNetworkInterface m_networkInterface;
MonitorMode m_monitorMode = MonitorModeMac;
bool m_macAddressSet = false;
bool m_macAddressManufacturerSet = false;
bool m_addressSet = false;
bool m_hostNameSet = false;
bool m_networkInterfaceSet = false;
bool m_forceComplete = false;
};
QDebug operator<<(QDebug debug, const NetworkDeviceInfo &networkDeviceInfo);
#endif // NETWORKDEVICEINFO_H

View File

@ -56,20 +56,21 @@ int NetworkDeviceInfos::indexFromHostAddress(const QHostAddress &address)
return -1;
}
int NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress)
QList<int> NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress)
{
return indexFromMacAddress(MacAddress(macAddress));
}
int NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress)
QList<int> NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress)
{
QList<int> indices;
for (int i = 0; i < size(); i++) {
if (MacAddress(at(i).macAddress()) == macAddress) {
return i;
if (at(i).macAddressInfos().hasMacAddress(macAddress)) {
indices << i;
}
}
return -1;
return indices;
}
bool NetworkDeviceInfos::hasHostAddress(const QHostAddress &address)
@ -79,12 +80,12 @@ bool NetworkDeviceInfos::hasHostAddress(const QHostAddress &address)
bool NetworkDeviceInfos::hasMacAddress(const QString &macAddress)
{
return indexFromMacAddress(macAddress) >= 0;
return !indexFromMacAddress(macAddress).isEmpty();
}
bool NetworkDeviceInfos::hasMacAddress(const MacAddress &macAddress)
{
return indexFromMacAddress(macAddress) >= 0;
return !indexFromMacAddress(macAddress).isEmpty();
}
NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address) const
@ -98,36 +99,45 @@ NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address) const
return NetworkDeviceInfo();
}
NetworkDeviceInfo NetworkDeviceInfos::get(const QString &macAddress) const
{
foreach (const NetworkDeviceInfo &networkDeviceInfo, *this) {
if (networkDeviceInfo.macAddress() == macAddress) {
return networkDeviceInfo;
}
}
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)
void NetworkDeviceInfos::removeHostAddress(const QHostAddress &address)
{
for (int i = 0; i < size(); i++) {
if (MacAddress(at(i).macAddress()) == macAddress) {
if (at(i).address() == address) {
remove(i);
}
}
}
// NetworkDeviceInfo NetworkDeviceInfos::get(const QString &macAddress) const
// {
// foreach (const NetworkDeviceInfo &networkDeviceInfo, *this) {
// if (networkDeviceInfo.macAddress() == macAddress) {
// return networkDeviceInfo;
// }
// }
// 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) {

View File

@ -39,25 +39,25 @@
class LIBNYMEA_EXPORT NetworkDeviceInfos : public QVector<NetworkDeviceInfo>
{
public:
explicit NetworkDeviceInfos();
NetworkDeviceInfos(const QVector<NetworkDeviceInfo> &other);
int indexFromHostAddress(const QHostAddress &address);
int indexFromMacAddress(const QString &macAddress);
int indexFromMacAddress(const MacAddress &macAddress);
QList<int> indexFromMacAddress(const QString &macAddress);
QList<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) const;
NetworkDeviceInfo get(const QString &macAddress) const;
NetworkDeviceInfo get(const MacAddress &macAddress) const;
// NetworkDeviceInfo get(const QString &macAddress) const;
// NetworkDeviceInfo get(const MacAddress &macAddress) const;
void removeMacAddress(const QString &macAddress);
void removeMacAddress(const MacAddress &macAddress);
// void removeMacAddress(const QString &macAddress);
// void removeMacAddress(const MacAddress &macAddress);
void removeHostAddress(const QHostAddress &address);
void sortNetworkDevices();

View File

@ -42,8 +42,9 @@ QDebug operator<<(QDebug dbg, NetworkDeviceMonitor *networkDeviceMonitor)
QDebugStateSaver saver(dbg);
dbg.nospace() << "NetworkDeviceMonitor(" << networkDeviceMonitor->macAddress().toString();
if (!networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer().isEmpty())
dbg.nospace() << " - " << networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer();
// FIXME
// if (!networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer().isEmpty())
// dbg.nospace() << " - " << networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer();
dbg.nospace() << ", " << networkDeviceMonitor->networkDeviceInfo().address().toString();
dbg.nospace() << ", " << (networkDeviceMonitor->reachable() ? "reachable" : "not reachable");

View File

@ -13,7 +13,7 @@ isEmpty(NYMEA_VERSION) {
JSON_PROTOCOL_VERSION_MAJOR=8
JSON_PROTOCOL_VERSION_MINOR=1
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
LIBNYMEA_API_VERSION_MAJOR=8
LIBNYMEA_API_VERSION_MAJOR=9
LIBNYMEA_API_VERSION_MINOR=0
LIBNYMEA_API_VERSION_PATCH=0
LIBNYMEA_API_VERSION="$${LIBNYMEA_API_VERSION_MAJOR}.$${LIBNYMEA_API_VERSION_MINOR}.$${LIBNYMEA_API_VERSION_PATCH}"