Commit 679c508c authored by Alessio Bonfiglio's avatar Alessio Bonfiglio Committed by Arjen Hiemstra
Browse files

Add more network sensor

Starting to use plasma system monitor on a daily basis, I noticed the lack of some sensors, so I decided to implement the ones I needed the most. In this first pr I focused on some network sensors: IPv4/6 gateway, subnetmask/prefix length and dns servers. The work should be completed, but I still left a FIXME for the case of the dns sensors when we are not using network manager to obtaining the information: the point is that I don't even think there exists a reliable way to retrieve the currently used dns in general, so those sensors return something only when networkmanager is available.
parent 43dc782a
Pipeline #174666 passed with stage
in 1 minute and 38 seconds
# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
# SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it>
set(KSYSGUARD_NETWORK_PLUGIN_SOURCES
NetworkPlugin.cpp
......@@ -22,7 +23,7 @@ if (KF5NetworkManagerQt_FOUND)
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_sources(ksystemstats_plugin_network PRIVATE RtNetlinkBackend.cpp)
target_link_libraries(ksystemstats_plugin_network PRIVATE ${NL_LIBRARIES})
target_link_libraries(ksystemstats_plugin_network PRIVATE ${NL_LIBRARIES} Qt::Network)
target_include_directories(ksystemstats_plugin_network PRIVATE ${NL_INCLUDE_DIRS})
endif()
......
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
* SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
......@@ -26,10 +27,42 @@ NetworkDevice::NetworkDevice(const QString &id, const QString &name)
m_ipv4Sensor->setShortName(i18nc("@title Short of IPv4 Address", "IPv4"));
m_ipv4Sensor->setPrefix(name);
m_ipv4GatewaySensor = new KSysGuard::SensorProperty(QStringLiteral("ipv4gateway"), i18nc("@title", "IPv4 Gateway"), this);
m_ipv4GatewaySensor->setShortName(i18nc("@title Short of IPv4 Gateway", "IPv4 Gateway"));
m_ipv4GatewaySensor->setPrefix(name);
m_ipv4SubnetMaskSensor = new KSysGuard::SensorProperty(QStringLiteral("ipv4subnet"), i18nc("@title", "IPv4 Subnet Mask"), this);
m_ipv4SubnetMaskSensor->setShortName(i18nc("@title Short of IPv4 Subnet Mask", "IPv4 Subnet Mask"));
m_ipv4SubnetMaskSensor->setPrefix(name);
m_ipv4WithPrefixLengthSensor = new KSysGuard::SensorProperty(QStringLiteral("ipv4withPrefixLength"), i18nc("@title", "IPv4 with Prefix Length"), this);
m_ipv4WithPrefixLengthSensor->setShortName(i18nc("@title Short of IPv4 Prefix Length", "IPv4"));
m_ipv4WithPrefixLengthSensor->setPrefix(name);
m_ipv4DNSSensor = new KSysGuard::SensorProperty(QStringLiteral("ipv4dns"), i18nc("@title", "IPv4 DNS"), this);
m_ipv4DNSSensor->setShortName(i18nc("@title Short of IPv4 DNS", "IPv4 DNS"));
m_ipv4DNSSensor->setPrefix(name);
m_ipv6Sensor = new KSysGuard::SensorProperty(QStringLiteral("ipv6address"), i18nc("@title", "IPv6 Address"), this);
m_ipv6Sensor->setShortName(i18nc("@title Short of IPv6 Address", "IPv6"));
m_ipv6Sensor->setPrefix(name);
m_ipv6GatewaySensor = new KSysGuard::SensorProperty(QStringLiteral("ipv6gateway"), i18nc("@title", "IPv6 Gateway"), this);
m_ipv6GatewaySensor->setShortName(i18nc("@title Short of IPv6 Gateway", "IPv6 Gateway"));
m_ipv6GatewaySensor->setPrefix(name);
m_ipv6SubnetMaskSensor = new KSysGuard::SensorProperty(QStringLiteral("ipv6subnet"), i18nc("@title", "IPv6 Subnet Mask"), this);
m_ipv6SubnetMaskSensor->setShortName(i18nc("@title Short of IPv6 Subnet Mask", "IPv6 Subnet Mask"));
m_ipv6SubnetMaskSensor->setPrefix(name);
m_ipv6WithPrefixLengthSensor = new KSysGuard::SensorProperty(QStringLiteral("ipv6withPrefixLength"), i18nc("@title", "IPv6 with Prefix Length"), this);
m_ipv6WithPrefixLengthSensor->setShortName(i18nc("@title Short of IPv6 Prefix Length", "IPv6"));
m_ipv6WithPrefixLengthSensor->setPrefix(name);
m_ipv6DNSSensor = new KSysGuard::SensorProperty(QStringLiteral("ipv6dns"), i18nc("@title", "IPv6 DNS"), this);
m_ipv6DNSSensor->setShortName(i18nc("@title Short of IPv6 DNS", "IPv6 DNS"));
m_ipv6DNSSensor->setPrefix(name);
m_downloadSensor = new KSysGuard::SensorProperty(QStringLiteral("download"), i18nc("@title", "Download Rate"), 0, this);
m_downloadSensor->setShortName(i18nc("@title Short for Download Rate", "Download"));
m_downloadSensor->setUnit(KSysGuard::UnitByteRate);
......
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
* SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
......@@ -20,7 +21,15 @@ protected:
KSysGuard::SensorProperty *m_networkSensor = nullptr;
KSysGuard::SensorProperty *m_signalSensor = nullptr;
KSysGuard::SensorProperty *m_ipv4Sensor = nullptr;
KSysGuard::SensorProperty *m_ipv4GatewaySensor = nullptr;
KSysGuard::SensorProperty *m_ipv4SubnetMaskSensor = nullptr;
KSysGuard::SensorProperty *m_ipv4WithPrefixLengthSensor = nullptr;
KSysGuard::SensorProperty *m_ipv4DNSSensor = nullptr;
KSysGuard::SensorProperty *m_ipv6Sensor = nullptr;
KSysGuard::SensorProperty *m_ipv6GatewaySensor = nullptr;
KSysGuard::SensorProperty *m_ipv6SubnetMaskSensor = nullptr;
KSysGuard::SensorProperty *m_ipv6WithPrefixLengthSensor = nullptr;
KSysGuard::SensorProperty *m_ipv6DNSSensor = nullptr;
KSysGuard::SensorProperty *m_downloadSensor = nullptr;
KSysGuard::SensorProperty *m_uploadSensor = nullptr;
KSysGuard::SensorProperty *m_downloadBitsSensor = nullptr;
......
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
* SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "NetworkManagerBackend.h"
#include <numeric>
#include <QTimer>
#include <QString>
#include <QDebug>
#include <NetworkManagerQt/Manager>
......@@ -28,7 +32,15 @@ NetworkManagerDevice::NetworkManagerDevice(const QString &id, QSharedPointer<Net
m_networkSensor->setPrefix(name());
m_signalSensor->setPrefix(name());
m_ipv4Sensor->setPrefix(name());
m_ipv4GatewaySensor->setPrefix(name());
m_ipv4SubnetMaskSensor->setPrefix(name());
m_ipv4WithPrefixLengthSensor->setPrefix(name());
m_ipv4DNSSensor->setPrefix(name());
m_ipv6Sensor->setPrefix(name());
m_ipv6GatewaySensor->setPrefix(name());
m_ipv6SubnetMaskSensor->setPrefix(name());
m_ipv6WithPrefixLengthSensor->setPrefix(name());
m_ipv6DNSSensor->setPrefix(name());
m_downloadSensor->setPrefix(name());
m_uploadSensor->setPrefix(name());
m_downloadBitsSensor->setPrefix(name());
......@@ -142,16 +154,47 @@ void NetworkManagerDevice::update()
setName(m_device->activeConnection()->connection()->name());
m_networkSensor->setValue(name());
auto dnsAccumulationFunction = [](QString &a, const QHostAddress& b) { return std::move(a).append(", ").append(b.toString()); };
if (m_device->ipV4Config().isValid()) {
m_ipv4Sensor->setValue(m_device->ipV4Config().addresses().at(0).ip().toString());
auto ipv4 = m_device->ipV4Config().addresses().at(0).ip().toString();
m_ipv4Sensor->setValue(ipv4);
m_ipv4GatewaySensor->setValue(m_device->ipV4Config().gateway());
m_ipv4SubnetMaskSensor->setValue(m_device->ipV4Config().addresses().at(0).netmask().toString());
m_ipv4WithPrefixLengthSensor->setValue(static_cast<QString>(ipv4 + '/' + QString::number(m_device->ipV4Config().addresses().at(0).prefixLength())));
auto dnsList = m_device->ipV4Config().nameservers();
auto dnsListString = QString();
if(!dnsList.isEmpty()) {
dnsListString = std::accumulate(std::next(dnsList.begin()), dnsList.end(), dnsList.at(0).toString(),
dnsAccumulationFunction);
}
m_ipv4DNSSensor->setValue(dnsListString);
} else {
m_ipv4Sensor->setValue(QString{});
m_ipv4GatewaySensor->setValue(QString{});
m_ipv4SubnetMaskSensor->setValue(QString{});
m_ipv4WithPrefixLengthSensor->setValue(QString{});
m_ipv4DNSSensor->setValue(QString{});
}
if (m_device->ipV6Config().isValid()) {
m_ipv6Sensor->setValue(m_device->ipV6Config().addresses().at(0).ip().toString());
auto ipv6 = m_device->ipV6Config().addresses().at(0).ip().toString();
m_ipv6Sensor->setValue(ipv6);
m_ipv6GatewaySensor->setValue(m_device->ipV6Config().gateway());
m_ipv6SubnetMaskSensor->setValue(m_device->ipV6Config().addresses().at(0).netmask().toString());
m_ipv6WithPrefixLengthSensor->setValue(static_cast<QString>(ipv6 + '/' + QString::number(m_device->ipV6Config().addresses().at(0).prefixLength())));
auto dnsList = m_device->ipV6Config().nameservers();
auto dnsListString = QString();
if(!dnsList.isEmpty()) {
dnsListString = std::accumulate(std::next(dnsList.begin()), dnsList.end(), dnsList.at(0).toString(),
dnsAccumulationFunction);
}
m_ipv6DNSSensor->setValue(dnsListString);
} else {
m_ipv6Sensor->setValue(QString{});
m_ipv6GatewaySensor->setValue(QString{});
m_ipv6SubnetMaskSensor->setValue(QString{});
m_ipv6WithPrefixLengthSensor->setValue(QString{});
m_ipv6DNSSensor->setValue(QString{});
}
}
......
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
* SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
* SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
......@@ -11,10 +12,14 @@
#include <QDir>
#include <QFile>
#include <QNetworkAddressEntry>
#include <QHostAddress>
#include <QString>
#include <array>
#include <netlink/netlink.h>
#include <netlink/route/addr.h>
#include <netlink/route/route.h>
#include <netlink/route/link.h>
#include <arpa/inet.h>
......@@ -42,9 +47,13 @@ RtNetlinkDevice::RtNetlinkDevice(const QString &id)
connect(property, &KSysGuard::SensorProperty::subscribedChanged, this, resetStatistics);
}
connect(this, &RtNetlinkDevice::disconnected, this, resetStatistics);
// FIXME: find the currently used dns servers
m_ipv4DNSSensor->setValue(QString{});
m_ipv6DNSSensor->setValue(QString{});
}
void RtNetlinkDevice::update(rtnl_link *link, nl_cache *address_cache, qint64 elapsedTime)
void RtNetlinkDevice::update(rtnl_link *link, nl_cache *address_cache, nl_cache *route_cache, qint64 elapsedTime)
{
const bool isConnected = rtnl_link_get_operstate(link) == IF_OPER_UP;
if (isConnected && !m_connected) {
......@@ -76,23 +85,76 @@ void RtNetlinkDevice::update(rtnl_link *link, nl_cache *address_cache, qint64 el
m_totalUploadSensor->setValue(uploadedBytes);
m_ipv4Sensor->setValue(QString());
m_ipv4SubnetMaskSensor->setValue(QString{});
m_ipv4WithPrefixLengthSensor->setValue(QString{});
m_ipv6Sensor->setValue(QString());
m_ipv6SubnetMaskSensor->setValue(QString{});
m_ipv6WithPrefixLengthSensor->setValue(QString{});
auto filterAddress = rtnl_addr_alloc();
rtnl_addr_set_ifindex(filterAddress, rtnl_link_get_ifindex(link));
nl_cache_foreach_filter(address_cache, reinterpret_cast<nl_object*>(filterAddress), [] (nl_object *object, void *arg) {
auto self = static_cast<RtNetlinkDevice *>(arg);
rtnl_addr *address = reinterpret_cast<rtnl_addr *>(object);
if (rtnl_addr_get_family(address) == AF_INET && self->m_ipv4Sensor->value().toString().isEmpty()) {
int prefixLen = rtnl_addr_get_prefixlen(address);
auto dummyAddress = QNetworkAddressEntry(); // conveniently used to compute the subnet mask
if (rtnl_addr_get_family(address) == AF_INET) {
if(self->m_ipv4Sensor->value().toString().isEmpty()) {
char buffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET, nl_addr_get_binary_addr(rtnl_addr_get_local(address)), buffer, INET_ADDRSTRLEN);
auto ipv4 = QString::fromLatin1(buffer);
self->m_ipv4Sensor->setValue(ipv4);
if(self->m_ipv4WithPrefixLengthSensor->value().toString().isEmpty()) {
self->m_ipv4WithPrefixLengthSensor->setValue(static_cast<QString>(ipv4 + '/' + QString::number(prefixLen)));
}
}
if(self->m_ipv4SubnetMaskSensor->value().toString().isEmpty()) {
dummyAddress.setIp(QHostAddress::AnyIPv4);
dummyAddress.setPrefixLength(prefixLen);
self->m_ipv4SubnetMaskSensor->setValue(dummyAddress.netmask().toString());
}
} else if (rtnl_addr_get_family(address) == AF_INET6) {
if(self->m_ipv6Sensor->value().toString().isEmpty()) {
char buffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, nl_addr_get_binary_addr(rtnl_addr_get_local(address)), buffer, INET6_ADDRSTRLEN);
auto ipv6 = QString::fromLatin1(buffer);
self->m_ipv6Sensor->setValue(ipv6);
if(self->m_ipv6WithPrefixLengthSensor->value().toString().isEmpty()) {
self->m_ipv6WithPrefixLengthSensor->setValue(static_cast<QString>(ipv6 + '/' + QString::number(prefixLen)));
}
}
if(self->m_ipv6SubnetMaskSensor->value().toString().isEmpty()) {
dummyAddress.setIp(QHostAddress::AnyIPv6);
dummyAddress.setPrefixLength(prefixLen);
self->m_ipv6SubnetMaskSensor->setValue(dummyAddress.netmask().toString());
}
}
}, this);
m_ipv4GatewaySensor->setValue(QString());
m_ipv6GatewaySensor->setValue(QString());
// The gateway is found using a filter on the destination address with size = 0
auto dst = nl_addr_build(AF_INET, NULL, 0);
auto routeFilter = rtnl_route_alloc();
rtnl_route_set_iif(routeFilter, rtnl_link_get_ifindex(link));
rtnl_route_set_dst(routeFilter, dst);
nl_cache_foreach_filter(route_cache, reinterpret_cast<nl_object*>(routeFilter), [] (nl_object *object, void *arg) {
auto self = static_cast<RtNetlinkDevice *>(arg);
auto route = reinterpret_cast<rtnl_route *>(object);
if (rtnl_route_get_family(route) == AF_INET && self->m_ipv4GatewaySensor->value().toString().isEmpty()) {
char buffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET, nl_addr_get_binary_addr(rtnl_addr_get_local(address)), buffer, INET_ADDRSTRLEN);
self->m_ipv4Sensor->setValue(QString::fromLatin1(buffer));
} else if (rtnl_addr_get_family(address) == AF_INET6 && self->m_ipv6Sensor->value().toString().isEmpty()) {
inet_ntop(AF_INET, nl_addr_get_binary_addr(rtnl_route_nh_get_gateway(rtnl_route_nexthop_n(route,0))), buffer, INET_ADDRSTRLEN);
self->m_ipv4GatewaySensor->setValue(QString::fromLatin1(buffer));
} else if (rtnl_route_get_family(route) == AF_INET6 && self->m_ipv6GatewaySensor->value().toString().isEmpty()) {
char buffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, nl_addr_get_binary_addr(rtnl_addr_get_local(address)), buffer, INET6_ADDRSTRLEN);
self->m_ipv6Sensor->setValue(QString::fromLatin1(buffer));
inet_ntop(AF_INET6, nl_addr_get_binary_addr(rtnl_route_nh_get_gateway(rtnl_route_nexthop_n(route,0))), buffer, INET6_ADDRSTRLEN);
self->m_ipv6GatewaySensor->setValue(QString::fromLatin1(buffer));
}
}, this);
rtnl_addr_put(filterAddress);
nl_addr_put(dst);
rtnl_route_put(routeFilter);
}
RtNetlinkBackend::RtNetlinkBackend(QObject *parent)
......@@ -127,7 +189,7 @@ void RtNetlinkBackend::stop()
void RtNetlinkBackend::update()
{
const qint64 elapsedTime = m_updateTimer.restart();
nl_cache *link_cache, *address_cache;
nl_cache *link_cache, *address_cache, *route_cache;
int error = rtnl_link_alloc_cache(m_socket.get(), AF_UNSPEC, &link_cache);
if (error != 0) {
qWarning() << nl_geterror(error);
......@@ -138,6 +200,11 @@ void RtNetlinkBackend::update()
qWarning() << nl_geterror(error);
return;
}
error = rtnl_route_alloc_cache(m_socket.get(), AF_UNSPEC, 0, &route_cache);
if (error != 0) {
qWarning() << nl_geterror(error);
return;
}
for (nl_object *object = nl_cache_get_first(link_cache); object != nullptr; object = nl_cache_get_next(object)) {
auto link = reinterpret_cast<rtnl_link *>(object);
......@@ -156,8 +223,9 @@ void RtNetlinkBackend::update()
connect(device, &RtNetlinkDevice::connected, this, [device, this] { Q_EMIT deviceAdded(device); });
connect(device, &RtNetlinkDevice::disconnected, this, [device, this] { Q_EMIT deviceRemoved(device); });
}
m_devices[name]->update(link, address_cache, elapsedTime);
m_devices[name]->update(link, address_cache, route_cache, elapsedTime);
}
nl_cache_free(link_cache);
nl_cache_free(address_cache);
nl_cache_free(route_cache);
}
/*
* SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
* SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
......@@ -22,7 +23,7 @@ class RtNetlinkDevice : public NetworkDevice
Q_OBJECT
public:
RtNetlinkDevice(const QString &id);
void update(rtnl_link *link, nl_cache *address_cache, qint64 elapsedTime);
void update(rtnl_link *link, nl_cache *address_cache, nl_cache *route_cache, qint64 elapsedTime);
Q_SIGNALS:
void connected();
void disconnected();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment