tst_QNetworkInterface: don't force an IPv4 link-local address

Windows apparently has special code to deal with those addresses. If all
your network interfaces are up and have acquired addresses, then the
link-local network at 169.254.0.0/16 is unreachable. I had never caught
this because both my Windows VM and my bare metal Windows have inactive
interfaces (like the Bluetooth PAN one) and, when inactive, Windows
assigns a link-local address. But in the CI, the interface(s) were all
up and running, causing this issue.

Unix systems don't treat IPv4 link-local any differently, so they always
worked, so long as any interface was up and there had to be one to reach
the network test server.

This commit reworks the test to add test addresses based on the
addresses found on up & running interfaces (see tst_qudpsocket.cpp). For
IPv4, we flip the bits in the local portion of the address. For IPv6, we
add a node with an address I generated randomly (non-universal), with
the same scope.

Fixes: QTBUG-65667
Change-Id: I568dea4813b448fe9ba6fffd15dd9f482db34991
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
bb10
Thiago Macieira 2019-12-05 16:02:56 -08:00
parent 29adc0eed9
commit c4b0ff4855
2 changed files with 30 additions and 20 deletions

View File

@ -1,4 +0,0 @@
# QTBUG-65667
[localAddress:linklocal-ipv4]
msvc-2015 ci
msvc-2017 ci

View File

@ -215,31 +215,45 @@ void tst_QNetworkInterface::loopbackIPv6()
}
void tst_QNetworkInterface::localAddress_data()
{
bool ipv6 = isIPv6Working();
QTest::addColumn<QHostAddress>("target");
QTest::newRow("localhost-ipv4") << QHostAddress(QHostAddress::LocalHost);
if (isIPv6Working())
if (ipv6)
QTest::newRow("localhost-ipv6") << QHostAddress(QHostAddress::LocalHostIPv6);
QTest::newRow("test-server") << QtNetworkSettings::serverIP();
// Since we don't actually transmit anything, we can list any IPv4 address
// and it should work. But we're using a linklocal address so that this
// test can pass even machines that failed to reach a DHCP server.
QTest::newRow("linklocal-ipv4") << QHostAddress("169.254.0.1");
QSet<QHostAddress> added;
const QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
for (const QNetworkInterface &iface : ifaces) {
if ((iface.flags() & QNetworkInterface::IsUp) == 0)
continue;
const QList<QNetworkAddressEntry> addrs = iface.addressEntries();
for (const QNetworkAddressEntry &entry : addrs) {
QHostAddress addr = entry.ip();
if (addr.isLoopback())
continue; // added above
if (isIPv6Working()) {
// On the other hand, we can't list just any IPv6 here. It's very
// likely that this machine has not received a route via ICMPv6-RA or
// DHCPv6, so it won't have a global route. On some OSes, IPv6 may be
// enabled per interface, so we need to know which ones work.
const QList<QHostAddress> addrs = QNetworkInterface::allAddresses();
for (const QHostAddress &addr : addrs) {
QString scope = addr.scopeId();
if (scope.isEmpty())
if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
// add an IPv4 address with bits in the host portion of the address flipped
quint32 ip4 = entry.ip().toIPv4Address();
addr.setAddress(ip4 ^ ~entry.netmask().toIPv4Address());
} else if (!ipv6 || entry.prefixLength() != 64) {
continue;
QTest::addRow("linklocal-ipv6-%s", qPrintable(scope))
<< QHostAddress("fe80::1234%" + scope);
} else {
// add a random node in this IPv6 network
quint64 randomid = qFromBigEndian(Q_UINT64_C(0x8f41f072e5733caa));
QIPv6Address ip6 = addr.toIPv6Address();
memcpy(&ip6[8], &randomid, sizeof(randomid));
addr.setAddress(ip6);
}
if (added.contains(addr))
continue;
added.insert(addr);
QTest::addRow("%s-%s", qPrintable(iface.name()), qPrintable(addr.toString())) << addr;
}
}
}