Qt 6.x
The Qt SDK
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
4#include "qnetworkinterface.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
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:
40
41 case ARPHRD_ETHER:
42 // check if it's a WiFi interface
43 if (qt_safe_ioctl(socket, SIOCGIWMODE, req) >= 0)
46
47 case ARPHRD_SLIP:
48 case ARPHRD_CSLIP:
49 case ARPHRD_SLIP6:
50 case ARPHRD_CSLIP6:
52
53 case ARPHRD_CAN:
55
56 case ARPHRD_PPP:
58
59 case ARPHRD_FDDI:
61
62 case ARPHRD_IEEE80211:
63 case ARPHRD_IEEE80211_PRISM:
64 case ARPHRD_IEEE80211_RADIOTAP:
66
69
70 case ARPHRD_PHONET:
73
74 case ARPHRD_6LOWPAN:
76
77 case ARPHRD_TUNNEL:
78 case ARPHRD_TUNNEL6:
79 case ARPHRD_NONE:
80 case ARPHRD_VOID:
82 }
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("Could not create AF_NETLINK socket");
96
97 // set buffer length
98 socklen_t len = sizeof(bufferSize);
99 setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufferSize, len);
100 }
101
102 ~NetlinkSocket()
103 {
104 if (sock != -1)
105 qt_safe_close(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 FirstArgument = typename FunctionTraits::Arguments::Car;
115
116 static int expectedTypeForRequest(int rtype)
117 {
118 static_assert(RTM_NEWADDR == RTM_GETADDR - 2);
119 static_assert(RTM_NEWLINK == RTM_GETLINK - 2);
120 Q_ASSERT(rtype == RTM_GETADDR || rtype == RTM_GETLINK);
121 return rtype - 2;
122 }
123
124 void operator()(int sock, nlmsghdr *hdr, char *buf, size_t bufsize, Lambda &&func)
125 {
126 // send the request
127 if (send(sock, hdr, hdr->nlmsg_len, 0) != ssize_t(hdr->nlmsg_len))
128 return;
129
130 // receive and parse the request
131 int expectedType = expectedTypeForRequest(hdr->nlmsg_type);
132 const bool isDump = hdr->nlmsg_flags & NLM_F_DUMP;
133 forever {
134 qsizetype len = recv(sock, buf, bufsize, 0);
135 hdr = reinterpret_cast<struct nlmsghdr *>(buf);
136 if (!NLMSG_OK(hdr, quint32(len)))
137 return;
138
139 auto arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
140 size_t payloadLen = NLMSG_PAYLOAD(hdr, 0);
141
142 // is this a multipart message?
143 Q_ASSERT(isDump == !!(hdr->nlmsg_flags & NLM_F_MULTI));
144 if (!isDump) {
145 // no, single message
146 if (hdr->nlmsg_type == expectedType && payloadLen >= sizeof(FirstArgument))
147 return void(func(arg, payloadLen));
148 } else {
149 // multipart, parse until done
150 do {
151 if (hdr->nlmsg_type == NLMSG_DONE)
152 return;
153 if (hdr->nlmsg_type != expectedType || payloadLen < sizeof(FirstArgument))
154 break;
155 func(arg, payloadLen);
156
157 // NLMSG_NEXT also updates the len variable
158 hdr = NLMSG_NEXT(hdr, len);
159 arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
160 payloadLen = NLMSG_PAYLOAD(hdr, 0);
161 } while (NLMSG_OK(hdr, quint32(len)));
162
163 if (len == 0)
164 continue; // get new datagram
165 }
166
167#ifndef QT_NO_DEBUG
168 if (NLMSG_OK(hdr, quint32(len)))
169 qWarning("QNetworkInterface/AF_NETLINK: received unknown packet type (%d) or too short (%u)",
170 hdr->nlmsg_type, hdr->nlmsg_len);
171 else
172 qWarning("QNetworkInterface/AF_NETLINK: received invalid packet with size %d", int(len));
173#endif
174 return;
175 }
176 }
177};
178
179template <typename Lambda>
180void processNetlinkRequest(int sock, struct nlmsghdr *hdr, char *buf, size_t bufsize, Lambda &&l)
181{
182 ProcessNetlinkRequest<Lambda>()(sock, hdr, buf, bufsize, std::forward<Lambda>(l));
183}
184}
185
187{
188 uint index = 0;
189 if (name.size() >= IFNAMSIZ)
190 return index;
191
192 int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
193 if (socket >= 0) {
194 struct ifreq req;
195 req.ifr_ifindex = 0;
196 strcpy(req.ifr_name, name.toLatin1().constData());
197
198 if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
199 index = req.ifr_ifindex;
201 }
202 return index;
203}
204
206{
207 int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
208 if (socket >= 0) {
209 struct ifreq req;
210 req.ifr_ifindex = index;
211
212 if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
214 return QString::fromLatin1(req.ifr_name);
215 }
217 }
218 return QString();
219}
220
222{
224 struct ifreq req;
225
226 // request all links
227 struct {
228 struct nlmsghdr req;
229 struct ifinfomsg ifi;
230 } ifi_req;
231 memset(&ifi_req, 0, sizeof(ifi_req));
232
233 ifi_req.req.nlmsg_len = sizeof(ifi_req);
234 ifi_req.req.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
235 ifi_req.req.nlmsg_type = RTM_GETLINK;
236
237 // parse the interfaces
238 processNetlinkRequest(sock, &ifi_req.req, buf, BufferSize, [&](ifinfomsg *ifi, size_t len) {
239 auto iface = new QNetworkInterfacePrivate;
240 iface->index = ifi->ifi_index;
241 iface->flags = convertFlags(ifi->ifi_flags);
242
243 // read attributes
244 auto rta = reinterpret_cast<struct rtattr *>(ifi + 1);
245 len -= sizeof(*ifi);
246 for ( ; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
247 int payloadLen = RTA_PAYLOAD(rta);
248 auto payloadPtr = reinterpret_cast<char *>(RTA_DATA(rta));
249
250 switch (rta->rta_type) {
251 case IFLA_ADDRESS: // link-level address
252 iface->hardwareAddress =
253 iface->makeHwAddress(payloadLen, reinterpret_cast<uchar *>(payloadPtr));
254 break;
255
256 case IFLA_IFNAME: // interface name
257 Q_ASSERT(payloadLen <= int(sizeof(req.ifr_name)));
258 memcpy(req.ifr_name, payloadPtr, payloadLen); // including terminating NUL
259 iface->name = QString::fromLatin1(payloadPtr, payloadLen - 1);
260 break;
261
262 case IFLA_MTU:
263 Q_ASSERT(payloadLen == sizeof(int));
264 iface->mtu = *reinterpret_cast<int *>(payloadPtr);
265 break;
266
267 case IFLA_OPERSTATE: // operational state
268 if (*payloadPtr != IF_OPER_UNKNOWN) {
269 // override the flag
270 iface->flags &= ~QNetworkInterface::IsRunning;
271 if (*payloadPtr == IF_OPER_UP)
272 iface->flags |= QNetworkInterface::IsRunning;
273 }
274 break;
275 }
276 }
277
278 if (Q_UNLIKELY(iface->name.isEmpty())) {
279 qWarning("QNetworkInterface: found interface %d with no name", iface->index);
280 delete iface;
281 } else {
282 iface->type = probeIfType(sock, &req, ifi->ifi_type);
283 result.append(iface);
284 }
285 });
286 return result;
287}
288
290{
291 // request all addresses
292 struct {
293 struct nlmsghdr req;
294 struct ifaddrmsg ifa;
295 } ifa_req;
296 memset(&ifa_req, 0, sizeof(ifa_req));
297
298 ifa_req.req.nlmsg_len = sizeof(ifa_req);
299 ifa_req.req.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
300 ifa_req.req.nlmsg_type = RTM_GETADDR;
301 ifa_req.req.nlmsg_seq = 1;
302
303 // parse the addresses
304 processNetlinkRequest(sock, &ifa_req.req, buf, BufferSize, [&](ifaddrmsg *ifa, size_t len) {
305 if (Q_UNLIKELY(ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6)) {
306 // unknown address types
307 return;
308 }
309
310 // find the interface this is relevant to
311 QNetworkInterfacePrivate *iface = nullptr;
312 for (auto candidate : std::as_const(result)) {
313 if (candidate->index != int(ifa->ifa_index))
314 continue;
315 iface = candidate;
316 break;
317 }
318
319 if (Q_UNLIKELY(!iface)) {
320 qWarning("QNetworkInterface/AF_NETLINK: found unknown interface with index %d", ifa->ifa_index);
321 return;
322 }
323
325 quint32 flags = ifa->ifa_flags; // may be overwritten by IFA_FLAGS
326
327 auto makeAddress = [=](uchar *ptr, int len) {
329 if (ifa->ifa_family == AF_INET) {
330 Q_ASSERT(len == 4);
331 addr.setAddress(qFromBigEndian<quint32>(ptr));
332 } else {
333 Q_ASSERT(len == 16);
334 addr.setAddress(ptr);
335
336 // do we need a scope ID?
337 if (addr.isLinkLocal())
338 addr.setScopeId(iface->name);
339 }
340 return addr;
341 };
342
343 // read attributes
344 auto rta = reinterpret_cast<struct rtattr *>(ifa + 1);
345 len -= sizeof(*ifa);
346 for ( ; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
347 int payloadLen = RTA_PAYLOAD(rta);
348 auto payloadPtr = reinterpret_cast<uchar *>(RTA_DATA(rta));
349
350 switch (rta->rta_type) {
351 case IFA_ADDRESS:
352 // Local address (all interfaces except for point-to-point)
353 if (entry.ip().isNull())
354 entry.setIp(makeAddress(payloadPtr, payloadLen));
355 break;
356
357 case IFA_LOCAL:
358 // Override the local address (point-to-point interfaces)
359 entry.setIp(makeAddress(payloadPtr, payloadLen));
360 break;
361
362 case IFA_BROADCAST:
363 Q_ASSERT(ifa->ifa_family == AF_INET);
364 entry.setBroadcast(makeAddress(payloadPtr, payloadLen));
365 break;
366
367 case IFA_CACHEINFO:
368 if (size_t(payloadLen) >= sizeof(ifa_cacheinfo)) {
369 auto cacheinfo = reinterpret_cast<ifa_cacheinfo *>(payloadPtr);
370 auto toDeadline = [](quint32 lifetime) -> QDeadlineTimer {
371 if (lifetime == quint32(-1))
373 return QDeadlineTimer(lifetime * 1000);
374 };
375 entry.setAddressLifetime(toDeadline(cacheinfo->ifa_prefered), toDeadline(cacheinfo->ifa_valid));
376 }
377 break;
378
379 case IFA_FLAGS:
380 Q_ASSERT(payloadLen == 4);
381 flags = qFromUnaligned<quint32>(payloadPtr);
382 break;
383 }
384 }
385
386 if (ifa->ifa_family == AF_INET6 && (ifa->ifa_flags & IFA_F_DADFAILED))
387 return;
388
389 // now handle flags
391 flags & IFA_F_TEMPORARY,
392 flags & IFA_F_DEPRECATED);
393
394
395 if (!entry.ip().isNull()) {
396 entry.setPrefixLength(ifa->ifa_prefixlen);
397 iface->addressEntries.append(entry);
398 }
399 });
400}
401
402QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
403{
404 // open netlink socket
406 NetlinkSocket sock(BufferSize);
407 if (Q_UNLIKELY(sock == -1))
408 return result;
409
411 char *buf = buffer.data();
412
415
416 return result;
417}
418
420
421#endif // QT_NO_NETWORKINTERFACE
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
static constexpr ForeverConstant Forever
The QHostAddress class provides an IP address.
Definition qlist.h:74
The QNetworkAddressEntry class stores one IP address supported by a network interface,...
static uint interfaceIndexFromName(const QString &name)
static QString interfaceNameFromIndex(uint index)
static void calculateDnsEligibility(QNetworkAddressEntry *entry, bool isTemporary, bool isDeprecated)
InterfaceType
Specifies the type of hardware (PHY layer, OSI level 1) this interface is, if it could be determined.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
#define Q_UNLIKELY(x)
static int qt_safe_close(int fd)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static constexpr int BufferSize
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:162
static ControlElement< T > * ptr(QWidget *widget)
#define AF_INET6
static int qt_safe_ioctl(int sockfd, unsigned long request, T arg)
static int qt_safe_socket(int domain, int type, int protocol, int flags=0)
Definition qnet_unix_p.h:45
#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)
GLuint index
[2]
GLenum GLuint buffer
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLsizei bufsize
GLuint name
GLenum func
Definition qopenglext.h:663
GLuint entry
GLenum GLsizei len
GLenum const void * addr
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
unsigned short ushort
Definition qtypes.h:28
if(qFloatDistance(a, b)<(1<< 7))
[0]
QTcpSocket sock
[2]
QTcpSocket * socket
[1]