1// Copyright (C) 2017 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qnetworkinterface.h"
5#include "qnetworkinterface_p.h"
6#include "qnetworkinterface_unix_p.h"
7
8#ifndef QT_NO_NETWORKINTERFACE
9
10#include <qendian.h>
11#include <qobjectdefs.h>
12#include <qvarlengtharray.h>
13
14// according to rtnetlink(7)
15#include <asm/types.h>
16#include <linux/if.h>
17#include <linux/if_arp.h>
18#include <linux/netlink.h>
19#include <linux/rtnetlink.h>
20#include <linux/wireless.h>
21#include <sys/socket.h>
22
23/* in case these aren't defined in linux/if_arp.h (added since 2.6.28) */
24#define ARPHRD_PHONET 820 /* v2.6.29: PhoNet media type */
25#define ARPHRD_PHONET_PIPE 821 /* v2.6.29: PhoNet pipe header */
26#define ARPHRD_IEEE802154 804 /* v2.6.31 */
27#define ARPHRD_6LOWPAN 825 /* v3.14: IPv6 over LoWPAN */
28
29QT_BEGIN_NAMESPACE
30
31enum {
32 BufferSize = 8192
33};
34
35static QNetworkInterface::InterfaceType probeIfType(int socket, struct ifreq *req, short arptype)
36{
37 switch (ushort(arptype)) {
38 case ARPHRD_LOOPBACK:
39 return QNetworkInterface::Loopback;
40
41 case ARPHRD_ETHER:
42 // check if it's a WiFi interface
43 if (qt_safe_ioctl(sockfd: socket, SIOCGIWMODE, arg: req) >= 0)
44 return QNetworkInterface::Wifi;
45 return QNetworkInterface::Ethernet;
46
47 case ARPHRD_SLIP:
48 case ARPHRD_CSLIP:
49 case ARPHRD_SLIP6:
50 case ARPHRD_CSLIP6:
51 return QNetworkInterface::Slip;
52
53 case ARPHRD_CAN:
54 return QNetworkInterface::CanBus;
55
56 case ARPHRD_PPP:
57 return QNetworkInterface::Ppp;
58
59 case ARPHRD_FDDI:
60 return QNetworkInterface::Fddi;
61
62 case ARPHRD_IEEE80211:
63 case ARPHRD_IEEE80211_PRISM:
64 case ARPHRD_IEEE80211_RADIOTAP:
65 return QNetworkInterface::Ieee80211;
66
67 case ARPHRD_IEEE802154:
68 return QNetworkInterface::Ieee802154;
69
70 case ARPHRD_PHONET:
71 case ARPHRD_PHONET_PIPE:
72 return QNetworkInterface::Phonet;
73
74 case ARPHRD_6LOWPAN:
75 return QNetworkInterface::SixLoWPAN;
76
77 case ARPHRD_TUNNEL:
78 case ARPHRD_TUNNEL6:
79 case ARPHRD_NONE:
80 case ARPHRD_VOID:
81 return QNetworkInterface::Virtual;
82 }
83 return QNetworkInterface::Unknown;
84}
85
86
87namespace {
88struct NetlinkSocket
89{
90 int sock;
91 NetlinkSocket(int bufferSize)
92 {
93 sock = qt_safe_socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
94 if (Q_UNLIKELY(sock == -1))
95 qErrnoWarning(msg: "Could not create AF_NETLINK socket");
96
97 // set buffer length
98 socklen_t len = sizeof(bufferSize);
99 setsockopt(fd: sock, SOL_SOCKET, SO_SNDBUF, optval: &bufferSize, optlen: len);
100 }
101
102 ~NetlinkSocket()
103 {
104 if (sock != -1)
105 qt_safe_close(fd: sock);
106 }
107
108 operator int() const { return sock; }
109};
110
111template <typename Lambda> struct ProcessNetlinkRequest
112{
113 using FunctionTraits = QtPrivate::FunctionPointer<decltype(&Lambda::operator())>;
114 using FirstArgumentPointer = typename FunctionTraits::Arguments::Car;
115 using FirstArgument = std::remove_pointer_t<FirstArgumentPointer>;
116 static_assert(std::is_pointer_v<FirstArgumentPointer>);
117 static_assert(std::is_aggregate_v<FirstArgument>);
118
119 static int expectedTypeForRequest(int rtype)
120 {
121 static_assert(RTM_NEWADDR == RTM_GETADDR - 2);
122 static_assert(RTM_NEWLINK == RTM_GETLINK - 2);
123 Q_ASSERT(rtype == RTM_GETADDR || rtype == RTM_GETLINK);
124 return rtype - 2;
125 }
126
127 void operator()(int sock, nlmsghdr *hdr, char *buf, size_t bufsize, Lambda &&func)
128 {
129 // send the request
130 if (send(fd: sock, buf: hdr, n: hdr->nlmsg_len, flags: 0) != ssize_t(hdr->nlmsg_len))
131 return;
132
133 // receive and parse the request
134 int expectedType = expectedTypeForRequest(rtype: hdr->nlmsg_type);
135 const bool isDump = hdr->nlmsg_flags & NLM_F_DUMP;
136 forever {
137 qsizetype len = recv(fd: sock, buf: buf, n: bufsize, flags: 0);
138 hdr = reinterpret_cast<struct nlmsghdr *>(buf);
139 if (!NLMSG_OK(hdr, quint32(len)))
140 return;
141
142 auto arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
143 size_t payloadLen = NLMSG_PAYLOAD(hdr, 0);
144
145 // is this a multipart message?
146 Q_ASSERT(isDump == !!(hdr->nlmsg_flags & NLM_F_MULTI));
147 if (!isDump) {
148 // no, single message
149 if (hdr->nlmsg_type == expectedType && payloadLen >= sizeof(FirstArgument))
150 return void(func(arg, payloadLen));
151 } else {
152 // multipart, parse until done
153 do {
154 if (hdr->nlmsg_type == NLMSG_DONE)
155 return;
156 if (hdr->nlmsg_type != expectedType || payloadLen < sizeof(FirstArgument))
157 break;
158 func(arg, payloadLen);
159
160 // NLMSG_NEXT also updates the len variable
161 hdr = NLMSG_NEXT(hdr, len);
162 arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
163 payloadLen = NLMSG_PAYLOAD(hdr, 0);
164 } while (NLMSG_OK(hdr, quint32(len)));
165
166 if (len == 0)
167 continue; // get new datagram
168 }
169
170#ifndef QT_NO_DEBUG
171 if (NLMSG_OK(hdr, quint32(len)))
172 qWarning(msg: "QNetworkInterface/AF_NETLINK: received unknown packet type (%d) or too short (%u)",
173 hdr->nlmsg_type, hdr->nlmsg_len);
174 else
175 qWarning(msg: "QNetworkInterface/AF_NETLINK: received invalid packet with size %d", int(len));
176#endif
177 return;
178 }
179 }
180};
181
182template <typename Lambda>
183void processNetlinkRequest(int sock, struct nlmsghdr *hdr, char *buf, size_t bufsize, Lambda &&l)
184{
185 ProcessNetlinkRequest<Lambda>()(sock, hdr, buf, bufsize, std::forward<Lambda>(l));
186}
187}
188
189uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
190{
191 uint index = 0;
192 if (name.size() >= IFNAMSIZ)
193 return index;
194
195 int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, protocol: 0);
196 if (socket >= 0) {
197 struct ifreq req;
198 req.ifr_ifindex = 0;
199 strcpy(dest: req.ifr_name, src: name.toLatin1().constData());
200
201 if (qt_safe_ioctl(sockfd: socket, SIOCGIFINDEX, arg: &req) >= 0)
202 index = req.ifr_ifindex;
203 qt_safe_close(fd: socket);
204 }
205 return index;
206}
207
208QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
209{
210 int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, protocol: 0);
211 if (socket >= 0) {
212 struct ifreq req;
213 req.ifr_ifindex = index;
214
215 if (qt_safe_ioctl(sockfd: socket, SIOCGIFNAME, arg: &req) >= 0) {
216 qt_safe_close(fd: socket);
217 return QString::fromLatin1(ba: req.ifr_name);
218 }
219 qt_safe_close(fd: socket);
220 }
221 return QString();
222}
223
224static QList<QNetworkInterfacePrivate *> getInterfaces(int sock, char *buf)
225{
226 QList<QNetworkInterfacePrivate *> result;
227 struct ifreq req;
228
229 // request all links
230 struct {
231 struct nlmsghdr req;
232 struct ifinfomsg ifi;
233 } ifi_req;
234 memset(s: &ifi_req, c: 0, n: sizeof(ifi_req));
235
236 ifi_req.req.nlmsg_len = sizeof(ifi_req);
237 ifi_req.req.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
238 ifi_req.req.nlmsg_type = RTM_GETLINK;
239
240 // parse the interfaces
241 processNetlinkRequest(sock, hdr: &ifi_req.req, buf, bufsize: BufferSize, l: [&](ifinfomsg *ifi, size_t len) {
242 auto iface = new QNetworkInterfacePrivate;
243 iface->index = ifi->ifi_index;
244 iface->flags = convertFlags(rawFlags: ifi->ifi_flags);
245
246 // read attributes
247 auto rta = reinterpret_cast<struct rtattr *>(ifi + 1);
248 len -= sizeof(*ifi);
249 for ( ; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
250 int payloadLen = RTA_PAYLOAD(rta);
251 auto payloadPtr = reinterpret_cast<char *>(RTA_DATA(rta));
252
253 switch (rta->rta_type) {
254 case IFLA_ADDRESS: // link-level address
255 iface->hardwareAddress =
256 iface->makeHwAddress(len: payloadLen, data: reinterpret_cast<uchar *>(payloadPtr));
257 break;
258
259 case IFLA_IFNAME: // interface name
260 Q_ASSERT(payloadLen <= int(sizeof(req.ifr_name)));
261 memcpy(dest: req.ifr_name, src: payloadPtr, n: payloadLen); // including terminating NUL
262 iface->name = QString::fromLatin1(str: payloadPtr, size: payloadLen - 1);
263 break;
264
265 case IFLA_MTU:
266 Q_ASSERT(payloadLen == sizeof(int));
267 iface->mtu = *reinterpret_cast<int *>(payloadPtr);
268 break;
269
270 case IFLA_OPERSTATE: // operational state
271 if (*payloadPtr != IF_OPER_UNKNOWN) {
272 // override the flag
273 iface->flags &= ~QNetworkInterface::IsRunning;
274 if (*payloadPtr == IF_OPER_UP)
275 iface->flags |= QNetworkInterface::IsRunning;
276 }
277 break;
278 }
279 }
280
281 if (Q_UNLIKELY(iface->name.isEmpty())) {
282 qWarning(msg: "QNetworkInterface: found interface %d with no name", iface->index);
283 delete iface;
284 } else {
285 iface->type = probeIfType(socket: sock, req: &req, arptype: ifi->ifi_type);
286 result.append(t: iface);
287 }
288 });
289 return result;
290}
291
292static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *> &result)
293{
294 // request all addresses
295 struct {
296 struct nlmsghdr req;
297 struct ifaddrmsg ifa;
298 } ifa_req;
299 memset(s: &ifa_req, c: 0, n: sizeof(ifa_req));
300
301 ifa_req.req.nlmsg_len = sizeof(ifa_req);
302 ifa_req.req.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
303 ifa_req.req.nlmsg_type = RTM_GETADDR;
304 ifa_req.req.nlmsg_seq = 1;
305
306 // parse the addresses
307 processNetlinkRequest(sock, hdr: &ifa_req.req, buf, bufsize: BufferSize, l: [&](ifaddrmsg *ifa, size_t len) {
308 if (Q_UNLIKELY(ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6)) {
309 // unknown address types
310 return;
311 }
312
313 // find the interface this is relevant to
314 QNetworkInterfacePrivate *iface = nullptr;
315 for (auto candidate : std::as_const(t&: result)) {
316 if (candidate->index != int(ifa->ifa_index))
317 continue;
318 iface = candidate;
319 break;
320 }
321
322 if (Q_UNLIKELY(!iface)) {
323 qWarning(msg: "QNetworkInterface/AF_NETLINK: found unknown interface with index %d", ifa->ifa_index);
324 return;
325 }
326
327 QNetworkAddressEntry entry;
328 quint32 flags = ifa->ifa_flags; // may be overwritten by IFA_FLAGS
329
330 auto makeAddress = [=](uchar *ptr, int len) {
331 QHostAddress addr;
332 if (ifa->ifa_family == AF_INET) {
333 Q_ASSERT(len == 4);
334 addr.setAddress(qFromBigEndian<quint32>(src: ptr));
335 } else {
336 Q_ASSERT(len == 16);
337 addr.setAddress(ptr);
338
339 // do we need a scope ID?
340 if (addr.isLinkLocal())
341 addr.setScopeId(iface->name);
342 }
343 return addr;
344 };
345
346 // read attributes
347 auto rta = reinterpret_cast<struct rtattr *>(ifa + 1);
348 len -= sizeof(*ifa);
349 for ( ; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
350 int payloadLen = RTA_PAYLOAD(rta);
351 auto payloadPtr = reinterpret_cast<uchar *>(RTA_DATA(rta));
352
353 switch (rta->rta_type) {
354 case IFA_ADDRESS:
355 // Local address (all interfaces except for point-to-point)
356 if (entry.ip().isNull())
357 entry.setIp(makeAddress(payloadPtr, payloadLen));
358 break;
359
360 case IFA_LOCAL:
361 // Override the local address (point-to-point interfaces)
362 entry.setIp(makeAddress(payloadPtr, payloadLen));
363 break;
364
365 case IFA_BROADCAST:
366 Q_ASSERT(ifa->ifa_family == AF_INET);
367 entry.setBroadcast(makeAddress(payloadPtr, payloadLen));
368 break;
369
370 case IFA_CACHEINFO:
371 if (size_t(payloadLen) >= sizeof(ifa_cacheinfo)) {
372 auto cacheinfo = reinterpret_cast<ifa_cacheinfo *>(payloadPtr);
373 auto toDeadline = [](quint32 lifetime) -> QDeadlineTimer {
374 if (lifetime == quint32(-1))
375 return QDeadlineTimer::Forever;
376 return QDeadlineTimer(lifetime * 1000);
377 };
378 entry.setAddressLifetime(preferred: toDeadline(cacheinfo->ifa_prefered), validity: toDeadline(cacheinfo->ifa_valid));
379 }
380 break;
381
382 case IFA_FLAGS:
383 Q_ASSERT(payloadLen == 4);
384 flags = qFromUnaligned<quint32>(src: payloadPtr);
385 break;
386 }
387 }
388
389 if (ifa->ifa_family == AF_INET6 && (ifa->ifa_flags & IFA_F_DADFAILED))
390 return;
391
392 // now handle flags
393 QNetworkInterfacePrivate::calculateDnsEligibility(entry: &entry,
394 isTemporary: flags & IFA_F_TEMPORARY,
395 isDeprecated: flags & IFA_F_DEPRECATED);
396
397
398 if (!entry.ip().isNull()) {
399 entry.setPrefixLength(ifa->ifa_prefixlen);
400 iface->addressEntries.append(t: entry);
401 }
402 });
403}
404
405QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
406{
407 // open netlink socket
408 QList<QNetworkInterfacePrivate *> result;
409 NetlinkSocket sock(BufferSize);
410 if (Q_UNLIKELY(sock == -1))
411 return result;
412
413 QByteArray buffer(BufferSize, Qt::Uninitialized);
414 char *buf = buffer.data();
415
416 result = getInterfaces(sock, buf);
417 getAddresses(sock, buf, result);
418
419 return result;
420}
421
422QT_END_NAMESPACE
423
424#endif // QT_NO_NETWORKINTERFACE
425

source code of qtbase/src/network/kernel/qnetworkinterface_linux.cpp