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:
Thiago Macieira 2017-08-08 16:40:29 -07:00
parent 5193ab97f4
commit a93437342b
7 changed files with 225 additions and 2 deletions

View File

@ -334,6 +334,122 @@ void QNetworkAddressEntry::setBroadcast(const QHostAddress &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
\brief The QNetworkInterface class provides a listing of the host's IP

View File

@ -49,7 +49,7 @@
QT_BEGIN_NAMESPACE
class QDeadlineTimer;
template<typename T> class QList;
class QNetworkAddressEntryPrivate;
@ -81,6 +81,14 @@ public:
QHostAddress broadcast() const;
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:
QScopedPointer<QNetworkAddressEntryPrivate> d;
};

View File

@ -386,6 +386,18 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
entry.setBroadcast(makeAddress(payloadPtr, payloadLen));
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:
Q_ASSERT(payloadLen == 4);
flags = qFromUnaligned<quint32>(payloadPtr);

View File

@ -54,6 +54,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/qnetworkinterface.h>
#include <QtCore/qatomic.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qlist.h>
#include <QtCore/qreadwritelock.h>
#include <QtCore/qstring.h>
@ -70,7 +71,11 @@ class QNetworkAddressEntryPrivate
public:
QHostAddress address;
QHostAddress broadcast;
QDeadlineTimer preferredLifetime = QDeadlineTimer::Forever;
QDeadlineTimer validityLifetime = QDeadlineTimer::Forever;
QNetmask netmask;
bool lifetimeKnown = false;
};
class QNetworkInterfacePrivate: public QSharedData

View File

@ -46,6 +46,10 @@
#ifndef QT_NO_NETWORKINTERFACE
#if defined(QT_NO_CLOCK_MONOTONIC)
# include "qdatetime.h"
#endif
#if defined(QT_LINUXBASE)
# define QT_NO_GETIFADDRS
#endif
@ -381,11 +385,19 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
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)
QT_BEGIN_INCLUDE_NAMESPACE
# include <net/if_dl.h>
# include <net/if_media.h>
# include <net/if_types.h>
# include <netinet/in_var.h>
QT_END_INCLUDE_NAMESPACE
static int openSocket(int &socket)
@ -471,6 +483,46 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
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
static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
@ -502,9 +554,14 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
return interfaces;
}
static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa, const char *ifname)
{
Q_UNUSED(entry);
Q_UNUSED(sa);
Q_UNUSED(ifname)
}
# endif
static QList<QNetworkInterfacePrivate *> interfaceListing()
{
QList<QNetworkInterfacePrivate *> interfaces;
@ -553,6 +610,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask, iface->index, iface->name));
if (iface->flags & QNetworkInterface::CanBroadcast)
entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr, iface->index, iface->name));
getAddressExtraInfo(&entry, ptr->ifa_addr, name.latin1());
iface->addressEntries << entry;
}

View File

@ -222,6 +222,14 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
QNetworkAddressEntry entry;
entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr));
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;
}
}

View File

@ -152,6 +152,12 @@ void tst_QNetworkInterface::dump()
<< " (" << qPrintable(e.netmask().toString()) << ')';
if (!e.broadcast().isNull())
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;
foreach (const QNetworkInterface &iface, ifaces) {
QVERIFY(iface.isValid());
QVERIFY2(!interfaceNames.contains(iface.name()),
"duplicate name = " + iface.name().toLocal8Bit());
interfaceNames << iface.name();
@ -171,6 +178,15 @@ void tst_QNetworkInterface::consistencyCheck()
"duplicate index = " + QByteArray::number(iface.index()));
if (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());
}
}
}