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 
probeIfType(int socket,struct ifreq * req,short arptype)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;
NetlinkSocket__anon8da63dcb0211::NetlinkSocket127     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 
~NetlinkSocket__anon8da63dcb0211::NetlinkSocket138     ~NetlinkSocket()
139     {
140         if (sock != -1)
141             qt_safe_close(sock);
142     }
143 
operator int__anon8da63dcb0211::NetlinkSocket144     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 
expectedTypeForRequest__anon8da63dcb0211::ProcessNetlinkRequest152     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 
operator ()__anon8da63dcb0211::ProcessNetlinkRequest160     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>
processNetlinkRequest(int sock,struct nlmsghdr * hdr,char * buf,size_t bufsize,Lambda && l)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 
interfaceIndexFromName(const QString & name)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 
interfaceNameFromIndex(uint index)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 
getInterfaces(int sock,char * buf)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 
getAddresses(int sock,char * buf,QList<QNetworkInterfacePrivate * > & result)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 
scan()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