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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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