File indexing completed on 2024-05-19 05:30:20
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> 0003 * SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de> 0004 * SPDX-FileCopyrightText: 2021 Alessio Bonfiglio <alessio.bonfiglio@mail.polimi.it> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "RtNetlinkBackend.h" 0010 0011 #include <systemstats/SysFsSensor.h> 0012 0013 #include <QNetworkAddressEntry> 0014 #include <QHostAddress> 0015 #include <QString> 0016 #include <array> 0017 0018 #include <netlink/netlink.h> 0019 #include <netlink/route/addr.h> 0020 #include <netlink/route/route.h> 0021 #include <netlink/route/link.h> 0022 0023 #include <arpa/inet.h> 0024 #include <linux/if_arp.h> 0025 #include <linux/if_link.h> 0026 #include <linux/rtnetlink.h> 0027 0028 static const QString devicesFolder = QStringLiteral("/sys/class/net"); 0029 0030 RtNetlinkDevice::RtNetlinkDevice(const QString &id) 0031 : NetworkDevice(id, id) 0032 { 0033 // Even though we have no sensor, we need to have a name for the grouped text on the front page 0034 // of plasma-systemmonitor 0035 m_networkSensor->setValue(id); 0036 0037 std::array<KSysGuard::SensorProperty*, 6> statisticSensors {m_downloadSensor, m_downloadBitsSensor, m_totalDownloadSensor, m_uploadSensor, m_uploadBitsSensor, m_totalUploadSensor}; 0038 auto resetStatistics = [this, statisticSensors]() { 0039 if (std::none_of(statisticSensors.begin(), statisticSensors.end(), [](auto property) {return property->isSubscribed();})) { 0040 m_totalDownloadSensor->setValue(0); 0041 m_totalUploadSensor->setValue(0); 0042 } 0043 }; 0044 for (auto property : statisticSensors) { 0045 connect(property, &KSysGuard::SensorProperty::subscribedChanged, this, resetStatistics); 0046 } 0047 connect(this, &RtNetlinkDevice::disconnected, this, resetStatistics); 0048 0049 // FIXME: find the currently used dns servers 0050 m_ipv4DNSSensor->setValue(QString{}); 0051 m_ipv6DNSSensor->setValue(QString{}); 0052 } 0053 0054 void RtNetlinkDevice::update(rtnl_link *link, nl_cache *address_cache, nl_cache *route_cache, qint64 elapsedTime) 0055 { 0056 const bool isConnected = rtnl_link_get_operstate(link) == IF_OPER_UP; 0057 if (isConnected && !m_connected) { 0058 m_connected = isConnected; 0059 Q_EMIT connected(); 0060 } else if (!isConnected && m_connected) { 0061 m_connected = isConnected; 0062 Q_EMIT disconnected(); 0063 } 0064 0065 if (!m_connected || !isSubscribed()) { 0066 return; 0067 } 0068 0069 const qulonglong downloadedBytes = rtnl_link_get_stat(link, RTNL_LINK_RX_BYTES); 0070 const qulonglong previousDownloadedBytes = m_totalDownloadSensor->value().toULongLong(); 0071 if (previousDownloadedBytes != 0) { 0072 m_downloadSensor->setValue((downloadedBytes - previousDownloadedBytes) * 1000 / elapsedTime); 0073 m_downloadBitsSensor->setValue((downloadedBytes - previousDownloadedBytes) * 1000 / elapsedTime * 8); 0074 } 0075 m_totalDownloadSensor->setValue(downloadedBytes); 0076 0077 const qulonglong uploadedBytes = rtnl_link_get_stat(link, RTNL_LINK_TX_BYTES); 0078 const qulonglong previousUploadedBytes = m_totalUploadSensor->value().toULongLong(); 0079 if (previousUploadedBytes != 0) { 0080 m_uploadSensor->setValue((uploadedBytes - previousUploadedBytes) * 1000 / elapsedTime); 0081 m_uploadBitsSensor->setValue((uploadedBytes - previousUploadedBytes) * 1000 / elapsedTime * 8); 0082 } 0083 m_totalUploadSensor->setValue(uploadedBytes); 0084 0085 m_ipv4Sensor->setValue(QString()); 0086 m_ipv4SubnetMaskSensor->setValue(QString{}); 0087 m_ipv4WithPrefixLengthSensor->setValue(QString{}); 0088 m_ipv6Sensor->setValue(QString()); 0089 m_ipv6SubnetMaskSensor->setValue(QString{}); 0090 m_ipv6WithPrefixLengthSensor->setValue(QString{}); 0091 auto filterAddress = rtnl_addr_alloc(); 0092 rtnl_addr_set_ifindex(filterAddress, rtnl_link_get_ifindex(link)); 0093 nl_cache_foreach_filter(address_cache, reinterpret_cast<nl_object*>(filterAddress), [] (nl_object *object, void *arg) { 0094 auto self = static_cast<RtNetlinkDevice *>(arg); 0095 rtnl_addr *address = reinterpret_cast<rtnl_addr *>(object); 0096 int prefixLen = rtnl_addr_get_prefixlen(address); 0097 auto dummyAddress = QNetworkAddressEntry(); // conveniently used to compute the subnet mask 0098 if (rtnl_addr_get_family(address) == AF_INET) { 0099 if(self->m_ipv4Sensor->value().toString().isEmpty()) { 0100 char buffer[INET6_ADDRSTRLEN]; 0101 inet_ntop(AF_INET, nl_addr_get_binary_addr(rtnl_addr_get_local(address)), buffer, INET_ADDRSTRLEN); 0102 auto ipv4 = QString::fromLatin1(buffer); 0103 self->m_ipv4Sensor->setValue(ipv4); 0104 if(self->m_ipv4WithPrefixLengthSensor->value().toString().isEmpty()) { 0105 self->m_ipv4WithPrefixLengthSensor->setValue(static_cast<QString>(ipv4 + '/' + QString::number(prefixLen))); 0106 } 0107 } 0108 if(self->m_ipv4SubnetMaskSensor->value().toString().isEmpty()) { 0109 dummyAddress.setIp(QHostAddress::AnyIPv4); 0110 dummyAddress.setPrefixLength(prefixLen); 0111 self->m_ipv4SubnetMaskSensor->setValue(dummyAddress.netmask().toString()); 0112 } 0113 } else if (rtnl_addr_get_family(address) == AF_INET6) { 0114 if(self->m_ipv6Sensor->value().toString().isEmpty()) { 0115 char buffer[INET6_ADDRSTRLEN]; 0116 inet_ntop(AF_INET6, nl_addr_get_binary_addr(rtnl_addr_get_local(address)), buffer, INET6_ADDRSTRLEN); 0117 auto ipv6 = QString::fromLatin1(buffer); 0118 self->m_ipv6Sensor->setValue(ipv6); 0119 if(self->m_ipv6WithPrefixLengthSensor->value().toString().isEmpty()) { 0120 self->m_ipv6WithPrefixLengthSensor->setValue(static_cast<QString>(ipv6 + '/' + QString::number(prefixLen))); 0121 } 0122 } 0123 if(self->m_ipv6SubnetMaskSensor->value().toString().isEmpty()) { 0124 dummyAddress.setIp(QHostAddress::AnyIPv6); 0125 dummyAddress.setPrefixLength(prefixLen); 0126 self->m_ipv6SubnetMaskSensor->setValue(dummyAddress.netmask().toString()); 0127 } 0128 } 0129 }, this); 0130 0131 m_ipv4GatewaySensor->setValue(QString()); 0132 m_ipv6GatewaySensor->setValue(QString()); 0133 // The gateway is found using a filter on the destination address with size = 0 0134 auto dst = nl_addr_build(AF_INET, NULL, 0); 0135 auto routeFilter = rtnl_route_alloc(); 0136 rtnl_route_set_iif(routeFilter, rtnl_link_get_ifindex(link)); 0137 rtnl_route_set_dst(routeFilter, dst); 0138 0139 nl_cache_foreach_filter(route_cache, reinterpret_cast<nl_object*>(routeFilter), [] (nl_object *object, void *arg) { 0140 auto self = static_cast<RtNetlinkDevice *>(arg); 0141 auto route = reinterpret_cast<rtnl_route *>(object); 0142 if (rtnl_route_get_family(route) == AF_INET && self->m_ipv4GatewaySensor->value().toString().isEmpty()) { 0143 char buffer[INET6_ADDRSTRLEN]; 0144 inet_ntop(AF_INET, nl_addr_get_binary_addr(rtnl_route_nh_get_gateway(rtnl_route_nexthop_n(route,0))), buffer, INET_ADDRSTRLEN); 0145 self->m_ipv4GatewaySensor->setValue(QString::fromLatin1(buffer)); 0146 } else if (rtnl_route_get_family(route) == AF_INET6 && self->m_ipv6GatewaySensor->value().toString().isEmpty()) { 0147 char buffer[INET6_ADDRSTRLEN]; 0148 inet_ntop(AF_INET6, nl_addr_get_binary_addr(rtnl_route_nh_get_gateway(rtnl_route_nexthop_n(route,0))), buffer, INET6_ADDRSTRLEN); 0149 self->m_ipv6GatewaySensor->setValue(QString::fromLatin1(buffer)); 0150 } 0151 }, this); 0152 0153 rtnl_addr_put(filterAddress); 0154 nl_addr_put(dst); 0155 rtnl_route_put(routeFilter); 0156 } 0157 0158 RtNetlinkBackend::RtNetlinkBackend(QObject *parent) 0159 : NetworkBackend(parent) 0160 , m_socket(nl_socket_alloc(), nl_socket_free) 0161 { 0162 nl_connect(m_socket.get(), NETLINK_ROUTE); 0163 } 0164 0165 RtNetlinkBackend::~RtNetlinkBackend() 0166 { 0167 qDeleteAll(m_devices); 0168 } 0169 0170 bool RtNetlinkBackend::isSupported() 0171 { 0172 return bool(m_socket); 0173 } 0174 0175 void RtNetlinkBackend::start() 0176 { 0177 if (!m_socket) { 0178 return; 0179 } 0180 update(); 0181 } 0182 0183 void RtNetlinkBackend::stop() 0184 { 0185 } 0186 0187 void RtNetlinkBackend::update() 0188 { 0189 const qint64 elapsedTime = m_updateTimer.restart(); 0190 nl_cache *link_cache, *address_cache, *route_cache; 0191 int error = rtnl_link_alloc_cache(m_socket.get(), AF_UNSPEC, &link_cache); 0192 if (error != 0) { 0193 qWarning() << nl_geterror(error); 0194 return; 0195 } 0196 error = rtnl_addr_alloc_cache(m_socket.get(), &address_cache); 0197 if (error != 0) { 0198 qWarning() << nl_geterror(error); 0199 return; 0200 } 0201 error = rtnl_route_alloc_cache(m_socket.get(), AF_UNSPEC, 0, &route_cache); 0202 if (error != 0) { 0203 qWarning() << nl_geterror(error); 0204 return; 0205 } 0206 0207 for (nl_object *object = nl_cache_get_first(link_cache); object != nullptr; object = nl_cache_get_next(object)) { 0208 auto link = reinterpret_cast<rtnl_link *>(object); 0209 if (rtnl_link_get_arptype(link) != ARPHRD_ETHER) { 0210 // FIXME Maybe this is to aggresive? On my machines wifi is also ether 0211 continue; 0212 } 0213 // Hardware devices do have an empty type 0214 if (qstrlen(rtnl_link_get_type(link)) != 0) { 0215 continue; 0216 } 0217 const auto name = QByteArray(rtnl_link_get_name(link)); 0218 if (!m_devices.contains(name)) { 0219 auto device = new RtNetlinkDevice(name); 0220 m_devices.insert(name, device); 0221 connect(device, &RtNetlinkDevice::connected, this, [device, this] { Q_EMIT deviceAdded(device); }); 0222 connect(device, &RtNetlinkDevice::disconnected, this, [device, this] { Q_EMIT deviceRemoved(device); }); 0223 } 0224 m_devices[name]->update(link, address_cache, route_cache, elapsedTime); 0225 } 0226 nl_cache_free(link_cache); 0227 nl_cache_free(address_cache); 0228 nl_cache_free(route_cache); 0229 } 0230 0231 #include "moc_RtNetlinkBackend.cpp"