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