From 9b4b2d9b204111fd71542dfa868093ec29d218fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 2 Dec 2024 16:18:23 +0100 Subject: [PATCH] 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 --- .../network/networkdevicediscoveryimpl.cpp | 308 ++++++++++-------- .../network/networkdevicediscoveryimpl.h | 8 +- .../networkdevicediscoveryreplyimpl.cpp | 215 +++++------- .../network/networkdevicediscoveryreplyimpl.h | 20 +- libnymea/integrations/thingdiscoveryinfo.h | 2 +- libnymea/libnymea.pro | 4 + libnymea/network/macaddressinfo.cpp | 103 ++++++ libnymea/network/macaddressinfo.h | 64 ++++ libnymea/network/macaddressinfos.cpp | 118 +++++++ libnymea/network/macaddressinfos.h | 60 ++++ libnymea/network/networkdevicediscovery.h | 2 +- .../network/networkdevicediscoveryreply.h | 6 - libnymea/network/networkdeviceinfo.cpp | 135 +++++--- libnymea/network/networkdeviceinfo.h | 43 ++- libnymea/network/networkdeviceinfos.cpp | 70 ++-- libnymea/network/networkdeviceinfos.h | 14 +- libnymea/network/networkdevicemonitor.cpp | 5 +- nymea.pro | 2 +- 18 files changed, 791 insertions(+), 388 deletions(-) create mode 100644 libnymea/network/macaddressinfo.cpp create mode 100644 libnymea/network/macaddressinfo.h create mode 100644 libnymea/network/macaddressinfos.cpp create mode 100644 libnymea/network/macaddressinfos.h diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp index 8d495cc6..60dbc1f5 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.cpp @@ -38,6 +38,8 @@ #include #include +#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(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 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()); diff --git a/libnymea-core/hardware/network/networkdevicediscoveryimpl.h b/libnymea-core/hardware/network/networkdevicediscoveryimpl.h index e2f115d2..859eb102 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryimpl.h +++ b/libnymea-core/hardware/network/networkdevicediscoveryimpl.h @@ -79,7 +79,7 @@ public: bool sendArpRequest(const QHostAddress &address) override; - QHash cache() const override; + NetworkDeviceInfos cache() const override; protected: void setEnabled(bool enabled) override; @@ -116,8 +116,12 @@ private: QHash m_monitorsReferenceCount; QHash m_lastSeen; + QHash m_macVendorCache; + + QHash m_infos; + QSettings *m_cacheSettings; - QHash m_networkInfoCache; + NetworkDeviceInfos m_networkInfoCache; void pingAllNetworkDevices(); diff --git a/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.cpp b/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.cpp index 8503c86b..453b2b12 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.cpp +++ b/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.cpp @@ -29,6 +29,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "networkdevicediscoveryreplyimpl.h" +#include "network/networkutils.h" #include "loggingcategories.h" #include @@ -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 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); + } } } } diff --git a/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.h b/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.h index e3c3e144..c41f2922 100644 --- a/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.h +++ b/libnymea-core/hardware/network/networkdevicediscoveryreplyimpl.h @@ -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 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 m_networkDeviceCache; + QHash 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 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); }; } diff --git a/libnymea/integrations/thingdiscoveryinfo.h b/libnymea/integrations/thingdiscoveryinfo.h index 48d03b45..63264f84 100644 --- a/libnymea/integrations/thingdiscoveryinfo.h +++ b/libnymea/integrations/thingdiscoveryinfo.h @@ -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(); diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index c183dda8..f288deb2 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -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 \ diff --git a/libnymea/network/macaddressinfo.cpp b/libnymea/network/macaddressinfo.cpp new file mode 100644 index 00000000..e41c0ead --- /dev/null +++ b/libnymea/network/macaddressinfo.cpp @@ -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 . +* +* 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 + +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; +} diff --git a/libnymea/network/macaddressinfo.h b/libnymea/network/macaddressinfo.h new file mode 100644 index 00000000..d4cc73e3 --- /dev/null +++ b/libnymea/network/macaddressinfo.h @@ -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 . +* +* 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 diff --git a/libnymea/network/macaddressinfos.cpp b/libnymea/network/macaddressinfos.cpp new file mode 100644 index 00000000..8bd171e6 --- /dev/null +++ b/libnymea/network/macaddressinfos.cpp @@ -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 . +* +* 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 &other) + : QVector(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; +} diff --git a/libnymea/network/macaddressinfos.h b/libnymea/network/macaddressinfos.h new file mode 100644 index 00000000..335b49ec --- /dev/null +++ b/libnymea/network/macaddressinfos.h @@ -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 . +* +* 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 +#include "macaddressinfo.h" + +class MacAddressInfos : public QVector +{ +public: + explicit MacAddressInfos(); + MacAddressInfos(const QVector &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 diff --git a/libnymea/network/networkdevicediscovery.h b/libnymea/network/networkdevicediscovery.h index c5450add..1934b960 100644 --- a/libnymea/network/networkdevicediscovery.h +++ b/libnymea/network/networkdevicediscovery.h @@ -67,7 +67,7 @@ public: virtual bool sendArpRequest(const QHostAddress &address) = 0; - virtual QHash cache() const = 0; + virtual NetworkDeviceInfos cache() const = 0; signals: void runningChanged(bool running); diff --git a/libnymea/network/networkdevicediscoveryreply.h b/libnymea/network/networkdevicediscoveryreply.h index bc7d205c..3e4bb952 100644 --- a/libnymea/network/networkdevicediscoveryreply.h +++ b/libnymea/network/networkdevicediscoveryreply.h @@ -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(); }; diff --git a/libnymea/network/networkdeviceinfo.cpp b/libnymea/network/networkdeviceinfo.cpp index 658f98e1..e6e6e6e3 100644 --- a/libnymea/network/networkdeviceinfo.cpp +++ b/libnymea/network/networkdeviceinfo.cpp @@ -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; } diff --git a/libnymea/network/networkdeviceinfo.h b/libnymea/network/networkdeviceinfo.h index 29f0418e..2d9e62df 100644 --- a/libnymea/network/networkdeviceinfo.h +++ b/libnymea/network/networkdeviceinfo.h @@ -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 #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 diff --git a/libnymea/network/networkdeviceinfos.cpp b/libnymea/network/networkdeviceinfos.cpp index a1421205..0f0c750a 100644 --- a/libnymea/network/networkdeviceinfos.cpp +++ b/libnymea/network/networkdeviceinfos.cpp @@ -56,20 +56,21 @@ int NetworkDeviceInfos::indexFromHostAddress(const QHostAddress &address) return -1; } -int NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress) +QList NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress) { return indexFromMacAddress(MacAddress(macAddress)); } -int NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress) +QList NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress) { + QList 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) { diff --git a/libnymea/network/networkdeviceinfos.h b/libnymea/network/networkdeviceinfos.h index 95fedd83..22fd8c4e 100644 --- a/libnymea/network/networkdeviceinfos.h +++ b/libnymea/network/networkdeviceinfos.h @@ -39,25 +39,25 @@ class LIBNYMEA_EXPORT NetworkDeviceInfos : public QVector { - public: explicit NetworkDeviceInfos(); NetworkDeviceInfos(const QVector &other); int indexFromHostAddress(const QHostAddress &address); - int indexFromMacAddress(const QString &macAddress); - int indexFromMacAddress(const MacAddress &macAddress); + QList indexFromMacAddress(const QString &macAddress); + QList 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(); diff --git a/libnymea/network/networkdevicemonitor.cpp b/libnymea/network/networkdevicemonitor.cpp index 775922b6..453e4612 100644 --- a/libnymea/network/networkdevicemonitor.cpp +++ b/libnymea/network/networkdevicemonitor.cpp @@ -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"); diff --git a/nymea.pro b/nymea.pro index bb1d03bf..748879b3 100644 --- a/nymea.pro +++ b/nymea.pro @@ -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}"