File indexing completed on 2024-05-12 16:59:53

0001 /*
0002  * SPDX-FileCopyrightText: 2001 Alexander Neundorf <neundorf@kde.org>
0003  * SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
0004  * SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "networkmodel.h"
0010 
0011 #include <arpa/inet.h>
0012 #include <netinet/in.h>
0013 #include <stdio.h>
0014 #include <sys/param.h>
0015 #include <sys/socket.h>
0016 #include <sys/types.h>
0017 #include <unistd.h>
0018 
0019 #include "config-nic.h"
0020 
0021 #include <array>
0022 
0023 #include <KLocalizedString>
0024 
0025 #include <QDebug>
0026 #include <QHash>
0027 
0028 #include <KPluginFactory>
0029 #include <net/if.h>
0030 #include <sys/ioctl.h>
0031 
0032 #include <ifaddrs.h>
0033 #include <netdb.h>
0034 
0035 using HostNameArray = std::array<char, NI_MAXHOST>;
0036 
0037 QString flags_tos(unsigned int flags);
0038 
0039 NetworkModel::NetworkModel(QObject *parent)
0040     : QAbstractListModel(parent)
0041 {
0042     update();
0043 }
0044 
0045 QVariant NetworkModel::data(const QModelIndex &index, int role) const
0046 {
0047     Q_UNUSED(role);
0048     if (index.isValid()) {
0049         const auto nic = m_nics.at(index.row());
0050 
0051         switch (role) {
0052         case NetworkModel::Roles::NameRole:
0053             return nic->name;
0054         case NetworkModel::Roles::AddrRole:
0055             return nic->addr;
0056         case NetworkModel::Roles::NetMaskRole:
0057             return nic->netmask;
0058         case NetworkModel::Roles::TypeRole:
0059             return nic->type;
0060         case NetworkModel::Roles::HWAddrRole:
0061             return nic->HWaddr;
0062         case NetworkModel::Roles::StateRole:
0063             return nic->state;
0064         }
0065     }
0066     return QVariant{};
0067 }
0068 
0069 QHash<int, QByteArray> NetworkModel::roleNames() const
0070 {
0071     return {{NetworkModel::Roles::NameRole, QByteArrayLiteral("name")},
0072             {NetworkModel::Roles::AddrRole, QByteArrayLiteral("address")},
0073             {NetworkModel::Roles::NetMaskRole, QByteArrayLiteral("netmask")},
0074             {NetworkModel::Roles::TypeRole, QByteArrayLiteral("type")},
0075             {NetworkModel::Roles::HWAddrRole, QByteArrayLiteral("hardwareAddress")},
0076             {NetworkModel::Roles::StateRole, QByteArrayLiteral("state")}};
0077 }
0078 
0079 int NetworkModel::rowCount(const QModelIndex &parent) const
0080 {
0081     Q_UNUSED(parent)
0082     return m_nics.size();
0083 }
0084 
0085 QList<NetworkModel::MyNIC *> findNICs();
0086 
0087 void NetworkModel::update()
0088 {
0089     beginResetModel();
0090     m_nics = findNICs();
0091     endResetModel();
0092 }
0093 
0094 // Convenience wrapper around sa_len being available or not.
0095 static int getNameInfo(struct sockaddr *addr, struct ifaddrs *ifa, HostNameArray &hostOut)
0096 {
0097     hostOut.fill(0);
0098 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
0099     return getnameinfo(addr, ifa->ifa_addr->sa_len, hostOut.data(), hostOut.size(), nullptr, 0, NI_NUMERICHOST);
0100 #else
0101     return getnameinfo(addr,
0102                        (ifa->ifa_addr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
0103                        hostOut.data(),
0104                        hostOut.size(),
0105                        nullptr,
0106                        0,
0107                        NI_NUMERICHOST);
0108 #endif
0109 }
0110 
0111 QList<NetworkModel::MyNIC *> findNICs()
0112 {
0113     QList<NetworkModel::MyNIC *> nl;
0114 
0115     struct ifaddrs *ifap = nullptr;
0116     if (getifaddrs(&ifap) != 0) {
0117         return nl;
0118     }
0119 
0120     for (auto *ifa = ifap; ifa; ifa = ifa->ifa_next) {
0121         if (!ifa->ifa_addr) {
0122             qDebug() << "stumbled over an interface without ifa_addr. You may wish to file a bug against kinfocenter" << ifa->ifa_name << ifa->ifa_flags;
0123             continue;
0124         }
0125 
0126         switch (ifa->ifa_addr->sa_family) {
0127         case AF_INET6:
0128         case AF_INET: {
0129             auto tmp = new NetworkModel::MyNIC;
0130             tmp->name = ifa->ifa_name;
0131 
0132             HostNameArray hostBuffer;
0133 
0134             getNameInfo(ifa->ifa_addr, ifa, hostBuffer);
0135             tmp->addr = hostBuffer.data();
0136 
0137             if (ifa->ifa_netmask != nullptr) {
0138                 getNameInfo(ifa->ifa_netmask, ifa, hostBuffer);
0139                 tmp->netmask = hostBuffer.data();
0140             }
0141 
0142             tmp->state = (ifa->ifa_flags & IFF_UP) ? true : false;
0143             tmp->type = flags_tos(ifa->ifa_flags);
0144 
0145             nl.append(tmp);
0146             break;
0147         }
0148         default:
0149             break;
0150         }
0151     }
0152 
0153     freeifaddrs(ifap);
0154     return nl;
0155 }
0156 
0157 QString flags_tos(unsigned int flags)
0158 {
0159     QString tmp;
0160     if (flags & IFF_POINTOPOINT) {
0161         tmp += i18nc("@item:intable Network type", "Point to Point");
0162     }
0163 
0164     if (flags & IFF_BROADCAST) {
0165         if (tmp.length()) {
0166             tmp += QLatin1String(", ");
0167         }
0168         tmp += i18nc("@item:intable Network type", "Broadcast");
0169     }
0170 
0171     if (flags & IFF_MULTICAST) {
0172         if (tmp.length()) {
0173             tmp += QLatin1String(", ");
0174         }
0175         tmp += i18nc("@item:intable Network type", "Multicast");
0176     }
0177 
0178     if (flags & IFF_LOOPBACK) {
0179         if (tmp.length()) {
0180             tmp += QLatin1String(", ");
0181         }
0182         tmp += i18nc("@item:intable Network type", "Loopback");
0183     }
0184     return tmp;
0185 }