Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnetworkinterface_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:trusted-data
5
6#include "qbytearray.h"
7#include "qset.h"
11#include "qalgorithms.h"
12
13#include <QtCore/private/qduplicatetracker_p.h>
14
15#ifndef QT_NO_NETWORKINTERFACE
16
17#if defined(QT_NO_CLOCK_MONOTONIC)
18# include "qdatetime.h"
19#endif
20
21#if QT_CONFIG(getifaddrs)
22# include <ifaddrs.h>
23#endif
24
25#ifdef QT_LINUXBASE
26# include <arpa/inet.h>
27# ifndef SIOCGIFBRDADDR
28# define SIOCGIFBRDADDR 0x8919
29# endif
30#endif // QT_LINUXBASE
31
32#include <qplatformdefs.h>
33
35
36static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QString &ifname = QString())
37{
38 QHostAddress address;
39 if (!sa)
40 return address;
41
42 if (sa->sa_family == AF_INET)
43 address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr));
44 else if (sa->sa_family == AF_INET6) {
45 address.setAddress(((sockaddr_in6 *)sa)->sin6_addr.s6_addr);
46 int scope = ((sockaddr_in6 *)sa)->sin6_scope_id;
47 if (scope && scope == ifindex) {
48 // this is the most likely scenario:
49 // a scope ID in a socket is that of the interface this address came from
50 address.setScopeId(ifname);
51 } else if (scope) {
52 address.setScopeId(QNetworkInterfaceManager::interfaceNameFromIndex(scope));
53 }
54 }
55 return address;
56}
57
58template <typename Req> [[maybe_unused]]
59static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_index) != 0, int> = 0)
60{
61 return req.ifr_index;
62}
63
64template <typename Req> [[maybe_unused]]
65static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_ifindex) != 0, int> = 0)
66{
67 return req.ifr_ifindex;
68}
69
70uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
71{
72#if QT_CONFIG(ipv6ifname)
73 return ::if_nametoindex(name.toLatin1().constData());
74#elif defined(SIOCGIFINDEX)
75 struct ifreq req;
76 int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
77 if (socket < 0)
78 return 0;
79
80 const QByteArray name8bit = name.toLatin1();
81 memset(&req, 0, sizeof(ifreq));
82 if (!name8bit.isNull())
83 memcpy(req.ifr_name, name8bit.data(), qMin(size_t(name8bit.length()) + 1, sizeof(req.ifr_name) - 1));
84
85 uint id = 0;
86 if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
87 id = ifreq_index(req);
88 qt_safe_close(socket);
89 return id;
90#else
91 Q_UNUSED(name);
92 return 0;
93#endif
94}
95
96QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
97{
98#if QT_CONFIG(ipv6ifname)
99 char buf[IF_NAMESIZE];
100 if (::if_indextoname(index, buf))
101 return QString::fromLatin1(buf);
102#elif defined(SIOCGIFNAME)
103 struct ifreq req;
104 int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
105 if (socket >= 0) {
106 memset(&req, 0, sizeof(ifreq));
107 ifreq_index(req) = index;
108 if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
109 qt_safe_close(socket);
110 return QString::fromLatin1(req.ifr_name);
111 }
112 qt_safe_close(socket);
113 }
114#endif
115 return QString::number(uint(index));
116}
117
118static int getMtu(int socket, struct ifreq *req)
119{
120#ifdef SIOCGIFMTU
121 if (qt_safe_ioctl(socket, SIOCGIFMTU, req) == 0)
122 return req->ifr_mtu;
123#endif
124 return 0;
125}
126
127#if !QT_CONFIG(getifaddrs)
128// getifaddrs not available
129
130static QSet<QByteArray> interfaceNames(int socket)
131{
132 QSet<QByteArray> result;
133#if !QT_CONFIG(ipv6ifname)
134 QByteArray storageBuffer;
135 struct ifconf interfaceList;
136 static const int STORAGEBUFFER_GROWTH = 256;
137
138 forever {
139 // grow the storage buffer
140 storageBuffer.resize(storageBuffer.size() + STORAGEBUFFER_GROWTH);
141 interfaceList.ifc_buf = storageBuffer.data();
142 interfaceList.ifc_len = storageBuffer.size();
143
144 // get the interface list
145 if (qt_safe_ioctl(socket, SIOCGIFCONF, &interfaceList) >= 0) {
146 if (int(interfaceList.ifc_len + sizeof(ifreq) + 64) < storageBuffer.size()) {
147 // if the buffer was big enough, break
148 storageBuffer.resize(interfaceList.ifc_len);
149 break;
150 }
151 } else {
152 // internal error
153 return result;
154 }
155 if (storageBuffer.size() > 100000) {
156 // out of space
157 return result;
158 }
159 }
160
161 int interfaceCount = interfaceList.ifc_len / sizeof(ifreq);
162 for (int i = 0; i < interfaceCount; ++i) {
163 QByteArray name = QByteArray(interfaceList.ifc_req[i].ifr_name);
164 if (!name.isEmpty())
165 result << name;
166 }
167
168 return result;
169#else
170 Q_UNUSED(socket);
171
172 // use if_nameindex
173 struct if_nameindex *interfaceList = ::if_nameindex();
174 for (struct if_nameindex *ptr = interfaceList; ptr && ptr->if_name; ++ptr)
175 result << ptr->if_name;
176
177 if_freenameindex(interfaceList);
178 return result;
179#endif
180}
181
182static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfacePrivate *> &interfaces,
183 struct ifreq &req)
184{
185 QNetworkInterfacePrivate *iface = nullptr;
186 int ifindex = 0;
187
188#if QT_CONFIG(ipv6ifname) || defined(SIOCGIFINDEX)
189 // Get the interface index
190# ifdef SIOCGIFINDEX
191 if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
192 ifindex = ifreq_index(req);
193# else
194 ifindex = if_nametoindex(req.ifr_name);
195# endif
196
197 // find the interface data
198 QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
199 for ( ; if_it != interfaces.end(); ++if_it)
200 if ((*if_it)->index == ifindex) {
201 // existing interface
202 iface = *if_it;
203 break;
204 }
205#else
206 Q_UNUSED(socket);
207 // Search by name
208 QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
209 for ( ; if_it != interfaces.end(); ++if_it)
210 if ((*if_it)->name == QLatin1StringView(req.ifr_name)) {
211 // existing interface
212 iface = *if_it;
213 break;
214 }
215#endif
216
217 if (!iface) {
218 // new interface, create data:
219 iface = new QNetworkInterfacePrivate;
220 iface->index = ifindex;
221 interfaces << iface;
222 }
223
224 return iface;
225}
226
227static QList<QNetworkInterfacePrivate *> interfaceListing()
228{
229 QList<QNetworkInterfacePrivate *> interfaces;
230
231 int socket;
232 if ((socket = qt_safe_socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1)
233 return interfaces; // error
234
235 QSet<QByteArray> names = interfaceNames(socket);
236 QSet<QByteArray>::ConstIterator it = names.constBegin();
237 for ( ; it != names.constEnd(); ++it) {
238 ifreq req;
239 memset(&req, 0, sizeof(ifreq));
240 if (!it->isNull())
241 memcpy(req.ifr_name, it->constData(), qMin(size_t(it->length()) + 1, sizeof(req.ifr_name) - 1));
242
243 QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req);
244
245#ifdef SIOCGIFNAME
246 // Get the canonical name
247 QByteArray oldName = req.ifr_name;
248 if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
249 iface->name = QString::fromLatin1(req.ifr_name);
250
251 // reset the name:
252 if (!oldName.isNull())
253 memcpy(req.ifr_name, oldName.constData(), qMin(size_t(oldName.length()) + 1, sizeof(req.ifr_name) - 1));
254 } else
255#endif
256 {
257 // use this name anyways
258 iface->name = QString::fromLatin1(req.ifr_name);
259 }
260
261 // Get interface flags
262 if (qt_safe_ioctl(socket, SIOCGIFFLAGS, &req) >= 0) {
263 iface->flags = convertFlags(req.ifr_flags);
264 }
265 iface->mtu = getMtu(socket, &req);
266
267#ifdef SIOCGIFHWADDR
268 // Get the HW address
269 if (qt_safe_ioctl(socket, SIOCGIFHWADDR, &req) >= 0) {
270 uchar *addr = (uchar *)req.ifr_addr.sa_data;
271 iface->hardwareAddress = iface->makeHwAddress(6, addr);
272 }
273#endif
274
275 // Get the address of the interface
276 QNetworkAddressEntry entry;
277 if (qt_safe_ioctl(socket, SIOCGIFADDR, &req) >= 0) {
278 sockaddr *sa = &req.ifr_addr;
279 entry.setIp(addressFromSockaddr(sa));
280
281 // Get the interface broadcast address
282 if (iface->flags & QNetworkInterface::CanBroadcast) {
283 if (qt_safe_ioctl(socket, SIOCGIFBRDADDR, &req) >= 0) {
284 sockaddr *sa = &req.ifr_addr;
285 if (sa->sa_family == AF_INET)
286 entry.setBroadcast(addressFromSockaddr(sa));
287 }
288 }
289
290 // Get the interface netmask
291 if (qt_safe_ioctl(socket, SIOCGIFNETMASK, &req) >= 0) {
292 sockaddr *sa = &req.ifr_addr;
293 entry.setNetmask(addressFromSockaddr(sa));
294 }
295
296 iface->addressEntries << entry;
297 }
298 }
299
300 ::close(socket);
301 return interfaces;
302}
303
304#else
305// use getifaddrs
306
307// platform-specific defs:
308# ifdef Q_OS_LINUX
309QT_BEGIN_INCLUDE_NAMESPACE
310# include <features.h>
311QT_END_INCLUDE_NAMESPACE
312# endif
313
314static int openSocket(int &socket)
315{
316 if (socket == -1)
317 socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
318 return socket;
319}
320
321# if defined(Q_OS_LINUX) && __GLIBC__ - 0 >= 2 && __GLIBC_MINOR__ - 0 >= 1 && !defined(QT_LINUXBASE)
322# include <netpacket/packet.h>
323
324static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
325{
326 Q_UNUSED(getMtu);
327 Q_UNUSED(openSocket);
328 QList<QNetworkInterfacePrivate *> interfaces;
329 QDuplicateTracker<QString> seenInterfaces;
330 QDuplicateTracker<int> seenIndexes;
331
332 // On Linux, glibc, uClibc and MUSL obtain the address listing via two
333 // netlink calls: first an RTM_GETLINK to obtain the interface listing,
334 // then one RTM_GETADDR to get all the addresses (uClibc implementation is
335 // copied from glibc; Bionic currently doesn't support getifaddrs). They
336 // synthesize AF_PACKET addresses from the RTM_GETLINK responses, which
337 // means by construction they currently show up first in the interface
338 // listing.
339 for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
340 if (ptr->ifa_addr && ptr->ifa_addr->sa_family == AF_PACKET) {
341 sockaddr_ll *sll = (sockaddr_ll *)ptr->ifa_addr;
342 QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
343 interfaces << iface;
344 iface->index = sll->sll_ifindex;
345 iface->name = QString::fromLatin1(ptr->ifa_name);
346 iface->flags = convertFlags(ptr->ifa_flags);
347 iface->hardwareAddress = iface->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr);
348
349 const bool sawIfaceIndex = seenIndexes.hasSeen(iface->index);
350 Q_ASSERT(!sawIfaceIndex);
351 (void)seenInterfaces.hasSeen(iface->name);
352 }
353 }
354
355 // see if we missed anything:
356 // - virtual interfaces with no HW address have no AF_PACKET
357 // - interface labels have no AF_PACKET, but shouldn't be shown as a new interface
358 for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
359 if (!ptr->ifa_addr || ptr->ifa_addr->sa_family != AF_PACKET) {
360 QString name = QString::fromLatin1(ptr->ifa_name);
361 if (seenInterfaces.hasSeen(name))
362 continue;
363
364 int ifindex = if_nametoindex(ptr->ifa_name);
365 if (seenIndexes.hasSeen(ifindex))
366 continue;
367
368 QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
369 interfaces << iface;
370 iface->name = name;
371 iface->flags = convertFlags(ptr->ifa_flags);
372 iface->index = ifindex;
373 }
374 }
375
376 return interfaces;
377}
378
379static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
380{
381 Q_UNUSED(entry);
382 Q_UNUSED(sa);
383 Q_UNUSED(ifname);
384}
385
386# elif defined(Q_OS_BSD4)
387QT_BEGIN_INCLUDE_NAMESPACE
388# include <net/if_dl.h>
389#if defined(QT_PLATFORM_UIKIT)
390# include "qnetworkinterface_uikit_p.h"
391# include <net/if_types.h>
392#else
393# include <net/if_media.h>
394# include <net/if_types.h>
395# include <netinet/in_var.h>
396#endif // QT_PLATFORM_UIKIT
397QT_END_INCLUDE_NAMESPACE
398
399static QNetworkInterface::InterfaceType probeIfType(int socket, int iftype, struct ifmediareq *req)
400{
401 // Determine the interface type.
402
403 // On Darwin, these are #defines, but on FreeBSD they're just an
404 // enum, so we can't #ifdef them. Use the authoritative list from
405 // https://www.iana.org/assignments/smi-numbers/smi-numbers.xhtml#smi-numbers-5
406 switch (iftype) {
407 case IFT_PPP:
408 return QNetworkInterface::Ppp;
409
410 case IFT_LOOP:
411 return QNetworkInterface::Loopback;
412
413 case IFT_SLIP:
414 return QNetworkInterface::Slip;
415
416 case 0x47: // IFT_IEEE80211
417 return QNetworkInterface::Ieee80211;
418
419 case IFT_IEEE1394:
420 return QNetworkInterface::Ieee1394;
421
422 case IFT_GIF:
423 case IFT_STF:
424 return QNetworkInterface::Virtual;
425 }
426
427 // For the remainder (including Ethernet), let's try SIOGIFMEDIA
428 req->ifm_count = 0;
429 if (qt_safe_ioctl(socket, SIOCGIFMEDIA, req) == 0) {
430 // see https://man.openbsd.org/ifmedia.4
431
432 switch (IFM_TYPE(req->ifm_current)) {
433 case IFM_ETHER:
434 return QNetworkInterface::Ethernet;
435
436#ifdef IFM_FDDI
437 case IFM_FDDI:
438 return QNetworkInterface::Fddi;
439#endif
440
441 case IFM_IEEE80211:
442 return QNetworkInterface::Ieee80211;
443 }
444 }
445
446 return QNetworkInterface::Unknown;
447}
448
449static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
450{
451 QList<QNetworkInterfacePrivate *> interfaces;
452 union {
453 struct ifmediareq mediareq;
454 struct ifreq req;
455 };
456 int socket = -1;
457 memset(&mediareq, 0, sizeof(mediareq));
458
459 // ensure both structs start with the name field, of size IFNAMESIZ
460 static_assert(sizeof(mediareq.ifm_name) == sizeof(req.ifr_name));
461 static_assert(offsetof(struct ifmediareq, ifm_name) == 0);
462 static_assert(offsetof(struct ifreq, ifr_name) == 0);
463
464 // on NetBSD we use AF_LINK and sockaddr_dl
465 // scan the list for that family
466 for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next)
467 if (ptr->ifa_addr && ptr->ifa_addr->sa_family == AF_LINK) {
468 QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
469 interfaces << iface;
470
471 sockaddr_dl *sdl = (sockaddr_dl *)ptr->ifa_addr;
472 iface->index = sdl->sdl_index;
473 iface->name = QString::fromLatin1(ptr->ifa_name);
474 iface->flags = convertFlags(ptr->ifa_flags);
475 iface->hardwareAddress = iface->makeHwAddress(sdl->sdl_alen, (uchar*)LLADDR(sdl));
476
477 qstrncpy(mediareq.ifm_name, ptr->ifa_name, sizeof(mediareq.ifm_name));
478 iface->type = probeIfType(openSocket(socket), sdl->sdl_type, &mediareq);
479 iface->mtu = getMtu(socket, &req);
480 }
481
482 if (socket != -1)
483 qt_safe_close(socket);
484 return interfaces;
485}
486
487static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
488{
489 // get IPv6 address lifetimes
490 if (sa->sa_family != AF_INET6)
491 return;
492
493 struct in6_ifreq ifr;
494
495 int s6 = qt_safe_socket(AF_INET6, SOCK_DGRAM, 0);
496 if (Q_UNLIKELY(s6 < 0)) {
497 qErrnoWarning("QNetworkInterface: could not create IPv6 socket");
498 return;
499 }
500
501 qstrncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
502
503 // get flags
504 ifr.ifr_addr = *reinterpret_cast<struct sockaddr_in6 *>(sa);
505 if (qt_safe_ioctl(s6, SIOCGIFAFLAG_IN6, &ifr) < 0) {
506 qt_safe_close(s6);
507 return;
508 }
509 int flags = ifr.ifr_ifru.ifru_flags6;
510 QNetworkInterfacePrivate::calculateDnsEligibility(entry,
511 flags & IN6_IFF_TEMPORARY,
512 flags & IN6_IFF_DEPRECATED);
513
514 // get lifetimes
515 ifr.ifr_addr = *reinterpret_cast<struct sockaddr_in6 *>(sa);
516 if (qt_safe_ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr) < 0) {
517 qt_safe_close(s6);
518 return;
519 }
520 qt_safe_close(s6);
521
522 auto toDeadline = [](time_t when) {
523 QDeadlineTimer deadline = QDeadlineTimer::Forever;
524 if (when) {
525#if defined(QT_NO_CLOCK_MONOTONIC)
526 // no monotonic clock
527 deadline.setPreciseRemainingTime(when - QDateTime::currentSecsSinceEpoch());
528#else
529 deadline.setPreciseDeadline(when);
530#endif
531 }
532 return deadline;
533 };
534 entry->setAddressLifetime(toDeadline(ifr.ifr_ifru.ifru_lifetime.ia6t_preferred),
535 toDeadline(ifr.ifr_ifru.ifru_lifetime.ia6t_expire));
536}
537
538# else // Generic version
539
540static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
541{
542 QList<QNetworkInterfacePrivate *> interfaces;
543 int socket = -1;
544
545 // make sure there's one entry for each interface
546 for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
547 // Get the interface index
548 int ifindex = if_nametoindex(ptr->ifa_name);
549
550 QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
551 for ( ; if_it != interfaces.end(); ++if_it)
552 if ((*if_it)->index == ifindex)
553 // this one has been added already
554 break;
555
556 if (if_it == interfaces.end()) {
557 // none found, create
559 interfaces << iface;
560
561 iface->index = ifindex;
562 iface->name = QString::fromLatin1(ptr->ifa_name);
563 iface->flags = convertFlags(ptr->ifa_flags);
564
565 if ((socket = openSocket(socket)) >= 0) {
566 struct ifreq ifr;
567 qstrncpy(ifr.ifr_name, ptr->ifa_name, sizeof(ifr.ifr_name));
568 iface->mtu = getMtu(socket, &ifr);
569 }
570 }
571 }
572
573 if (socket != -1)
574 qt_safe_close(socket);
575
576 return interfaces;
577}
578
579static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
580{
581 Q_UNUSED(entry);
582 Q_UNUSED(sa);
583 Q_UNUSED(ifname);
584}
585# endif
586
587static QList<QNetworkInterfacePrivate *> interfaceListing()
588{
589 QList<QNetworkInterfacePrivate *> interfaces;
590
591 ifaddrs *interfaceListing;
592 if (getifaddrs(&interfaceListing) == -1) {
593 // error
594 return interfaces;
595 }
596
597 interfaces = createInterfaces(interfaceListing);
598 for (ifaddrs *ptr = interfaceListing; ptr; ptr = ptr->ifa_next) {
599 // Find the interface
600 QLatin1StringView name(ptr->ifa_name);
601 QNetworkInterfacePrivate *iface = nullptr;
602 QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
603 for ( ; if_it != interfaces.end(); ++if_it)
604 if ((*if_it)->name == name) {
605 // found this interface already
606 iface = *if_it;
607 break;
608 }
609
610 if (!iface) {
611 // it may be an interface label, search by interface index
612 int ifindex = if_nametoindex(ptr->ifa_name);
613 for (if_it = interfaces.begin(); if_it != interfaces.end(); ++if_it)
614 if ((*if_it)->index == ifindex) {
615 // found this interface already
616 iface = *if_it;
617 break;
618 }
619 }
620
621 if (!iface) {
622 // skip all non-IP interfaces
623 continue;
624 }
625
627 entry.setIp(addressFromSockaddr(ptr->ifa_addr, iface->index, iface->name));
628 if (entry.ip().isNull())
629 // could not parse the address
630 continue;
631
632 entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask, iface->index, iface->name));
633 if (iface->flags & QNetworkInterface::CanBroadcast)
634 entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr, iface->index, iface->name));
635 getAddressExtraInfo(&entry, ptr->ifa_addr, name.latin1());
636
637 iface->addressEntries << entry;
638 }
639
640 freeifaddrs(interfaceListing);
641 return interfaces;
642}
643#endif
644
645QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
646{
647 return interfaceListing();
648}
649
650QT_END_NAMESPACE
651
652#endif // QT_NO_NETWORKINTERFACE
The QNetworkAddressEntry class stores one IP address supported by a network interface,...
#define AF_INET6
static QT_BEGIN_NAMESPACE QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex=0, const QString &ifname=QString())
static int getMtu(int socket, struct ifreq *req)
static auto & ifreq_index(Req &req, std::enable_if_t< sizeof(std::declval< Req >().ifr_index) !=0, int >=0)