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 | |
65 | QT_BEGIN_NAMESPACE |
66 | |
67 | enum { |
68 | BufferSize = 8192 |
69 | }; |
70 | |
71 | static 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 | |
123 | namespace { |
124 | struct 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 | |
147 | template <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 | static_assert(RTM_NEWADDR == RTM_GETADDR - 2); |
155 | 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 | |
215 | template <typename Lambda> |
216 | void 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 | |
222 | uint 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 | |
241 | QString 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 | |
257 | static 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 | |
325 | static 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 | if (ifa->ifa_family == AF_INET6 && (ifa->ifa_flags & IFA_F_DADFAILED)) |
423 | return; |
424 | |
425 | // now handle flags |
426 | QNetworkInterfacePrivate::calculateDnsEligibility(&entry, |
427 | flags & IFA_F_TEMPORARY, |
428 | flags & IFA_F_DEPRECATED); |
429 | |
430 | |
431 | if (!entry.ip().isNull()) { |
432 | entry.setPrefixLength(ifa->ifa_prefixlen); |
433 | iface->addressEntries.append(entry); |
434 | } |
435 | }); |
436 | } |
437 | |
438 | QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan() |
439 | { |
440 | // open netlink socket |
441 | QList<QNetworkInterfacePrivate *> result; |
442 | NetlinkSocket sock(BufferSize); |
443 | if (Q_UNLIKELY(sock == -1)) |
444 | return result; |
445 | |
446 | QByteArray buffer(BufferSize, Qt::Uninitialized); |
447 | char *buf = buffer.data(); |
448 | |
449 | result = getInterfaces(sock, buf); |
450 | getAddresses(sock, buf, result); |
451 | |
452 | return result; |
453 | } |
454 | |
455 | QT_END_NAMESPACE |
456 | |
457 | #endif // QT_NO_NETWORKINTERFACE |
458 | |