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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user