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