QNetworkInterface: add support for extracting address lifetime
[ChangeLog][QtNetwork][QNetworkInterface] Added preferredLifetime() and validityLifetime() to QNetworkAddressEntry that report the remaining lifetime of the address in the network interface. Change-Id: I292b84e2193979446e43344b0727642812cba630 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
5193ab97f4
commit
a93437342b
@ -334,6 +334,122 @@ void QNetworkAddressEntry::setBroadcast(const QHostAddress &newBroadcast)
|
|||||||
d->broadcast = newBroadcast;
|
d->broadcast = newBroadcast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Returns \c true if the address lifetime is known, \c false if not. If the
|
||||||
|
lifetime is not known, both preferredLifetime() and validityLifetime() will
|
||||||
|
return QDeadlineTimer::Forever.
|
||||||
|
|
||||||
|
\sa preferredLifetime(), validityLifetime(), setAddressLifetime(), clearAddressLifetime()
|
||||||
|
*/
|
||||||
|
bool QNetworkAddressEntry::isLifetimeKnown() const
|
||||||
|
{
|
||||||
|
return d->lifetimeKnown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Returns the deadline when this address becomes deprecated (no longer
|
||||||
|
preferred), if known. If the address lifetime is not known (see
|
||||||
|
isLifetimeKnown()), this function always returns QDeadlineTimer::Forever.
|
||||||
|
|
||||||
|
While an address is preferred, it may be used by the operating system as
|
||||||
|
the source address for new, outgoing packets. After it becomes deprecated,
|
||||||
|
it will remain valid for incoming packets for a while longer until finally
|
||||||
|
removed (see validityLifetime()).
|
||||||
|
|
||||||
|
\sa validityLifetime(), isLifetimeKnown(), setAddressLifetime(), clearAddressLifetime()
|
||||||
|
*/
|
||||||
|
QDeadlineTimer QNetworkAddressEntry::preferredLifetime() const
|
||||||
|
{
|
||||||
|
return d->preferredLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Returns the deadline when this address becomes invalid and will be removed
|
||||||
|
from the networking stack, if known. If the address lifetime is not known
|
||||||
|
(see isLifetimeKnown()), this function always returns
|
||||||
|
QDeadlineTimer::Forever.
|
||||||
|
|
||||||
|
While an address is valid, it will be accepted by the operating system as a
|
||||||
|
valid destination address for this machine. Whether it is used as a source
|
||||||
|
address for new, outgoing packets is controlled by, among other rules, the
|
||||||
|
preferred lifetime (see preferredLifetime()).
|
||||||
|
|
||||||
|
\sa preferredLifetime(), isLifetimeKnown(), setAddressLifetime(), clearAddressLifetime()
|
||||||
|
*/
|
||||||
|
QDeadlineTimer QNetworkAddressEntry::validityLifetime() const
|
||||||
|
{
|
||||||
|
return d->validityLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Sets both the preferred and valid lifetimes for this address to the \a
|
||||||
|
preferred and \a validity deadlines, respectively. After this call,
|
||||||
|
isLifetimeKnown() will return \c true, even if both parameters are
|
||||||
|
QDeadlineTimer::Forever.
|
||||||
|
|
||||||
|
\sa preferredLifetime(), validityLifetime(), isLifetimeKnown(), clearAddressLifetime()
|
||||||
|
*/
|
||||||
|
void QNetworkAddressEntry::setAddressLifetime(QDeadlineTimer preferred, QDeadlineTimer validity)
|
||||||
|
{
|
||||||
|
d->preferredLifetime = preferred;
|
||||||
|
d->validityLifetime = validity;
|
||||||
|
d->lifetimeKnown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Resets both the preferred and valid lifetimes for this address. After this
|
||||||
|
call, isLifetimeKnown() will return \c false.
|
||||||
|
|
||||||
|
\sa preferredLifetime(), validityLifetime(), isLifetimeKnown(), setAddressLifetime()
|
||||||
|
*/
|
||||||
|
void QNetworkAddressEntry::clearAddressLifetime()
|
||||||
|
{
|
||||||
|
d->preferredLifetime = QDeadlineTimer::Forever;
|
||||||
|
d->validityLifetime = QDeadlineTimer::Forever;
|
||||||
|
d->lifetimeKnown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Returns \c true if this address is permanent on this interface, \c false if
|
||||||
|
it's temporary. A permenant address is one which has no expiration time and
|
||||||
|
is often static (manually configured).
|
||||||
|
|
||||||
|
If this information could not be determined, this function returns \c true.
|
||||||
|
|
||||||
|
\note Depending on the operating system and the networking configuration
|
||||||
|
tool, it is possible for a temporary address to be interpreted as
|
||||||
|
permanent, if the tool did not inform the details correctly to the
|
||||||
|
operating system.
|
||||||
|
|
||||||
|
\sa isLifetimeKnown(), validityLifetime(), isTemporary()
|
||||||
|
*/
|
||||||
|
bool QNetworkAddressEntry::isPermanent() const
|
||||||
|
{
|
||||||
|
return d->validityLifetime.isForever();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn bool QNetworkAddressEntry::isTemporary() const
|
||||||
|
\since 5.11
|
||||||
|
|
||||||
|
Returns \c true if this address is temporary on this interface, \c false if
|
||||||
|
it's permanent.
|
||||||
|
|
||||||
|
\sa isLifetimeKnown(), validityLifetime(), isPermanent()
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QNetworkInterface
|
\class QNetworkInterface
|
||||||
\brief The QNetworkInterface class provides a listing of the host's IP
|
\brief The QNetworkInterface class provides a listing of the host's IP
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QDeadlineTimer;
|
||||||
template<typename T> class QList;
|
template<typename T> class QList;
|
||||||
|
|
||||||
class QNetworkAddressEntryPrivate;
|
class QNetworkAddressEntryPrivate;
|
||||||
@ -81,6 +81,14 @@ public:
|
|||||||
QHostAddress broadcast() const;
|
QHostAddress broadcast() const;
|
||||||
void setBroadcast(const QHostAddress &newBroadcast);
|
void setBroadcast(const QHostAddress &newBroadcast);
|
||||||
|
|
||||||
|
bool isLifetimeKnown() const;
|
||||||
|
QDeadlineTimer preferredLifetime() const;
|
||||||
|
QDeadlineTimer validityLifetime() const;
|
||||||
|
void setAddressLifetime(QDeadlineTimer preferred, QDeadlineTimer validity);
|
||||||
|
void clearAddressLifetime();
|
||||||
|
bool isPermanent() const;
|
||||||
|
bool isTemporary() const { return !isPermanent(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<QNetworkAddressEntryPrivate> d;
|
QScopedPointer<QNetworkAddressEntryPrivate> d;
|
||||||
};
|
};
|
||||||
|
@ -386,6 +386,18 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
|
|||||||
entry.setBroadcast(makeAddress(payloadPtr, payloadLen));
|
entry.setBroadcast(makeAddress(payloadPtr, payloadLen));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IFA_CACHEINFO:
|
||||||
|
if (size_t(payloadLen) >= sizeof(ifa_cacheinfo)) {
|
||||||
|
auto cacheinfo = reinterpret_cast<ifa_cacheinfo *>(payloadPtr);
|
||||||
|
auto toDeadline = [](quint32 lifetime) -> QDeadlineTimer {
|
||||||
|
if (lifetime == quint32(-1))
|
||||||
|
return QDeadlineTimer::Forever;
|
||||||
|
return QDeadlineTimer(lifetime * 1000);
|
||||||
|
};
|
||||||
|
entry.setAddressLifetime(toDeadline(cacheinfo->ifa_prefered), toDeadline(cacheinfo->ifa_valid));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case IFA_FLAGS:
|
case IFA_FLAGS:
|
||||||
Q_ASSERT(payloadLen == 4);
|
Q_ASSERT(payloadLen == 4);
|
||||||
flags = qFromUnaligned<quint32>(payloadPtr);
|
flags = qFromUnaligned<quint32>(payloadPtr);
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||||
#include <QtNetwork/qnetworkinterface.h>
|
#include <QtNetwork/qnetworkinterface.h>
|
||||||
#include <QtCore/qatomic.h>
|
#include <QtCore/qatomic.h>
|
||||||
|
#include <QtCore/qdeadlinetimer.h>
|
||||||
#include <QtCore/qlist.h>
|
#include <QtCore/qlist.h>
|
||||||
#include <QtCore/qreadwritelock.h>
|
#include <QtCore/qreadwritelock.h>
|
||||||
#include <QtCore/qstring.h>
|
#include <QtCore/qstring.h>
|
||||||
@ -70,7 +71,11 @@ class QNetworkAddressEntryPrivate
|
|||||||
public:
|
public:
|
||||||
QHostAddress address;
|
QHostAddress address;
|
||||||
QHostAddress broadcast;
|
QHostAddress broadcast;
|
||||||
|
QDeadlineTimer preferredLifetime = QDeadlineTimer::Forever;
|
||||||
|
QDeadlineTimer validityLifetime = QDeadlineTimer::Forever;
|
||||||
|
|
||||||
QNetmask netmask;
|
QNetmask netmask;
|
||||||
|
bool lifetimeKnown = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QNetworkInterfacePrivate: public QSharedData
|
class QNetworkInterfacePrivate: public QSharedData
|
||||||
|
@ -46,6 +46,10 @@
|
|||||||
|
|
||||||
#ifndef QT_NO_NETWORKINTERFACE
|
#ifndef QT_NO_NETWORKINTERFACE
|
||||||
|
|
||||||
|
#if defined(QT_NO_CLOCK_MONOTONIC)
|
||||||
|
# include "qdatetime.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(QT_LINUXBASE)
|
#if defined(QT_LINUXBASE)
|
||||||
# define QT_NO_GETIFADDRS
|
# define QT_NO_GETIFADDRS
|
||||||
#endif
|
#endif
|
||||||
@ -381,11 +385,19 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
|
|||||||
return interfaces;
|
return interfaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
|
||||||
|
{
|
||||||
|
Q_UNUSED(entry);
|
||||||
|
Q_UNUSED(sa);
|
||||||
|
Q_UNUSED(ifname)
|
||||||
|
}
|
||||||
|
|
||||||
# elif defined(Q_OS_BSD4)
|
# elif defined(Q_OS_BSD4)
|
||||||
QT_BEGIN_INCLUDE_NAMESPACE
|
QT_BEGIN_INCLUDE_NAMESPACE
|
||||||
# include <net/if_dl.h>
|
# include <net/if_dl.h>
|
||||||
# include <net/if_media.h>
|
# include <net/if_media.h>
|
||||||
# include <net/if_types.h>
|
# include <net/if_types.h>
|
||||||
|
# include <netinet/in_var.h>
|
||||||
QT_END_INCLUDE_NAMESPACE
|
QT_END_INCLUDE_NAMESPACE
|
||||||
|
|
||||||
static int openSocket(int &socket)
|
static int openSocket(int &socket)
|
||||||
@ -471,6 +483,46 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
|
|||||||
return interfaces;
|
return interfaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
|
||||||
|
{
|
||||||
|
// get IPv6 address lifetimes
|
||||||
|
if (sa->sa_family != AF_INET6)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct in6_ifreq ifr;
|
||||||
|
|
||||||
|
int s6 = qt_safe_socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
if (Q_UNLIKELY(s6 < 0)) {
|
||||||
|
qErrnoWarning("QNetworkInterface: could not create IPv6 socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
||||||
|
|
||||||
|
// get lifetimes
|
||||||
|
ifr.ifr_addr = *reinterpret_cast<struct sockaddr_in6 *>(sa);
|
||||||
|
if (qt_safe_ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr) < 0) {
|
||||||
|
qt_safe_close(s6);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qt_safe_close(s6);
|
||||||
|
|
||||||
|
auto toDeadline = [](time_t when) {
|
||||||
|
QDeadlineTimer deadline = QDeadlineTimer::Forever;
|
||||||
|
if (when) {
|
||||||
|
#if defined(QT_NO_CLOCK_MONOTONIC)
|
||||||
|
// no monotonic clock
|
||||||
|
deadline.setPreciseRemainingTime(when - QDateTime::currentSecsSinceEpoch());
|
||||||
|
#else
|
||||||
|
deadline.setPreciseDeadline(when);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return deadline;
|
||||||
|
};
|
||||||
|
entry->setAddressLifetime(toDeadline(ifr.ifr_ifru.ifru_lifetime.ia6t_preferred),
|
||||||
|
toDeadline(ifr.ifr_ifru.ifru_lifetime.ia6t_expire));
|
||||||
|
}
|
||||||
|
|
||||||
# else // Generic version
|
# else // Generic version
|
||||||
|
|
||||||
static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
|
static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
|
||||||
@ -502,9 +554,14 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
|
|||||||
return interfaces;
|
return interfaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
|
||||||
|
{
|
||||||
|
Q_UNUSED(entry);
|
||||||
|
Q_UNUSED(sa);
|
||||||
|
Q_UNUSED(ifname)
|
||||||
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
|
||||||
static QList<QNetworkInterfacePrivate *> interfaceListing()
|
static QList<QNetworkInterfacePrivate *> interfaceListing()
|
||||||
{
|
{
|
||||||
QList<QNetworkInterfacePrivate *> interfaces;
|
QList<QNetworkInterfacePrivate *> interfaces;
|
||||||
@ -553,6 +610,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
|
|||||||
entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask, iface->index, iface->name));
|
entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask, iface->index, iface->name));
|
||||||
if (iface->flags & QNetworkInterface::CanBroadcast)
|
if (iface->flags & QNetworkInterface::CanBroadcast)
|
||||||
entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr, iface->index, iface->name));
|
entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr, iface->index, iface->name));
|
||||||
|
getAddressExtraInfo(&entry, ptr->ifa_addr, name.latin1());
|
||||||
|
|
||||||
iface->addressEntries << entry;
|
iface->addressEntries << entry;
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,14 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
|
|||||||
QNetworkAddressEntry entry;
|
QNetworkAddressEntry entry;
|
||||||
entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr));
|
entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr));
|
||||||
entry.setPrefixLength(addr->OnLinkPrefixLength);
|
entry.setPrefixLength(addr->OnLinkPrefixLength);
|
||||||
|
|
||||||
|
auto toDeadline = [](ULONG lifetime) -> QDeadlineTimer {
|
||||||
|
if (lifetime == 0xffffffffUL)
|
||||||
|
return QDeadlineTimer::Forever;
|
||||||
|
return QDeadlineTimer(lifetime * 1000);
|
||||||
|
};
|
||||||
|
entry.setAddressLifetime(toDeadline(addr->ValidLifetime), toDeadline(addr->PreferredLifetime));
|
||||||
|
|
||||||
iface->addressEntries << entry;
|
iface->addressEntries << entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,12 @@ void tst_QNetworkInterface::dump()
|
|||||||
<< " (" << qPrintable(e.netmask().toString()) << ')';
|
<< " (" << qPrintable(e.netmask().toString()) << ')';
|
||||||
if (!e.broadcast().isNull())
|
if (!e.broadcast().isNull())
|
||||||
s.nospace() << " broadcast " << qPrintable(e.broadcast().toString());
|
s.nospace() << " broadcast " << qPrintable(e.broadcast().toString());
|
||||||
|
if (e.isLifetimeKnown()) {
|
||||||
|
#define printable(l) qPrintable(l.isForever() ? "forever" : QString::fromLatin1("%1ms").arg(l.remainingTime()))
|
||||||
|
s.nospace() << " preferred:" << printable(e.preferredLifetime())
|
||||||
|
<< " valid:" << printable(e.validityLifetime());
|
||||||
|
#undef printable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,6 +169,7 @@ void tst_QNetworkInterface::consistencyCheck()
|
|||||||
QVector<int> interfaceIndexes;
|
QVector<int> interfaceIndexes;
|
||||||
|
|
||||||
foreach (const QNetworkInterface &iface, ifaces) {
|
foreach (const QNetworkInterface &iface, ifaces) {
|
||||||
|
QVERIFY(iface.isValid());
|
||||||
QVERIFY2(!interfaceNames.contains(iface.name()),
|
QVERIFY2(!interfaceNames.contains(iface.name()),
|
||||||
"duplicate name = " + iface.name().toLocal8Bit());
|
"duplicate name = " + iface.name().toLocal8Bit());
|
||||||
interfaceNames << iface.name();
|
interfaceNames << iface.name();
|
||||||
@ -171,6 +178,15 @@ void tst_QNetworkInterface::consistencyCheck()
|
|||||||
"duplicate index = " + QByteArray::number(iface.index()));
|
"duplicate index = " + QByteArray::number(iface.index()));
|
||||||
if (iface.index())
|
if (iface.index())
|
||||||
interfaceIndexes << iface.index();
|
interfaceIndexes << iface.index();
|
||||||
|
|
||||||
|
const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
|
||||||
|
for (auto entry : addresses) {
|
||||||
|
QVERIFY(entry.ip().protocol() != QAbstractSocket::UnknownNetworkLayerProtocol);
|
||||||
|
if (!entry.preferredLifetime().isForever() || !entry.validityLifetime().isForever())
|
||||||
|
QVERIFY(entry.isLifetimeKnown());
|
||||||
|
if (!entry.validityLifetime().isForever())
|
||||||
|
QVERIFY(entry.isTemporary());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user