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