1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30
31#include <QtTest/QtTest>
32
33#include <qcoreapplication.h>
34#include <qnetworkinterface.h>
35#include <qudpsocket.h>
36#ifndef QT_NO_BEARERMANAGEMENT
37#include <QNetworkConfigurationManager>
38#include <QNetworkSession>
39#endif
40#include "../../../network-settings.h"
41#include "emulationdetector.h"
42
43Q_DECLARE_METATYPE(QHostAddress)
44
45class tst_QNetworkInterface : public QObject
46{
47 Q_OBJECT
48
49public:
50 tst_QNetworkInterface();
51 virtual ~tst_QNetworkInterface();
52
53 bool isIPv6Working();
54
55private slots:
56 void initTestCase();
57 void cleanupTestCase();
58 void dump();
59 void consistencyCheck();
60 void loopbackIPv4();
61 void loopbackIPv6();
62 void localAddress_data();
63 void localAddress();
64 void interfaceFromXXX_data();
65 void interfaceFromXXX();
66 void copyInvalidInterface();
67
68private:
69#ifndef QT_NO_BEARERMANAGEMENT
70 QNetworkConfigurationManager *netConfMan;
71 QNetworkConfiguration networkConfiguration;
72 QScopedPointer<QNetworkSession> networkSession;
73#endif
74};
75
76tst_QNetworkInterface::tst_QNetworkInterface()
77{
78}
79
80tst_QNetworkInterface::~tst_QNetworkInterface()
81{
82}
83
84bool tst_QNetworkInterface::isIPv6Working()
85{
86 QUdpSocket socket;
87 socket.connectToHost(address: QHostAddress::LocalHostIPv6, port: 1234);
88 return socket.state() == QAbstractSocket::ConnectedState || socket.waitForConnected(msecs: 100);
89}
90
91void tst_QNetworkInterface::initTestCase()
92{
93 if (!QtNetworkSettings::verifyTestNetworkSettings())
94 QSKIP("No network test server available");
95#ifndef QT_NO_BEARERMANAGEMENT
96 netConfMan = new QNetworkConfigurationManager(this);
97 if (netConfMan->capabilities()
98 & QNetworkConfigurationManager::NetworkSessionRequired) {
99 networkConfiguration = netConfMan->defaultConfiguration();
100 networkSession.reset(other: new QNetworkSession(networkConfiguration));
101 if (!networkSession->isOpen()) {
102 networkSession->open();
103 QVERIFY(networkSession->waitForOpened(30000));
104 }
105 }
106#endif
107}
108
109void tst_QNetworkInterface::cleanupTestCase()
110{
111#ifndef QT_NO_BEARERMANAGEMENT
112 if (networkSession && networkSession->isOpen()) {
113 networkSession->close();
114 }
115#endif
116}
117
118void tst_QNetworkInterface::dump()
119{
120 // This is for manual testing:
121 QList<QNetworkInterface> allInterfaces = QNetworkInterface::allInterfaces();
122 foreach (const QNetworkInterface &i, allInterfaces) {
123 QString flags;
124 if (i.flags() & QNetworkInterface::IsUp) flags += "Up,";
125 if (i.flags() & QNetworkInterface::IsRunning) flags += "Running,";
126 if (i.flags() & QNetworkInterface::CanBroadcast) flags += "Broadcast,";
127 if (i.flags() & QNetworkInterface::IsLoopBack) flags += "Loopback,";
128 if (i.flags() & QNetworkInterface::IsPointToPoint) flags += "PointToPoint,";
129 if (i.flags() & QNetworkInterface::CanMulticast) flags += "Multicast,";
130 flags.chop(n: 1); // drop last comma
131
132 QString friendlyName = i.humanReadableName();
133 if (friendlyName != i.name()) {
134 friendlyName.prepend(c: '(');
135 friendlyName.append(c: ')');
136 } else {
137 friendlyName.clear();
138 }
139 qDebug() << "Interface: " << i.name() << qPrintable(friendlyName);
140 QVERIFY(i.isValid());
141
142 qDebug() << " index: " << i.index();
143 qDebug() << " flags: " << qPrintable(flags);
144 qDebug() << " type: " << i.type();
145 qDebug() << " hw address:" << qPrintable(i.hardwareAddress());
146 qDebug() << " MTU: " << i.maximumTransmissionUnit();
147
148 int count = 0;
149 foreach (const QNetworkAddressEntry &e, i.addressEntries()) {
150 QDebug s = qDebug();
151 s.nospace() << " address "
152 << qSetFieldWidth(width: 2) << count++ << qSetFieldWidth(width: 0);
153 s.nospace() << ": " << qPrintable(e.ip().toString());
154 if (!e.netmask().isNull())
155 s.nospace() << '/' << e.prefixLength()
156 << " (" << qPrintable(e.netmask().toString()) << ')';
157 if (!e.broadcast().isNull())
158 s.nospace() << " broadcast " << qPrintable(e.broadcast().toString());
159 if (e.dnsEligibility() == QNetworkAddressEntry::DnsEligible)
160 s.nospace() << " dns-eligible";
161 else if (e.dnsEligibility() == QNetworkAddressEntry::DnsIneligible)
162 s.nospace() << " dns-ineligible";
163 if (e.isLifetimeKnown()) {
164#define printable(l) qPrintable(l.isForever() ? "forever" : QString::fromLatin1("%1ms").arg(l.remainingTime()))
165 s.nospace() << " preferred:" << printable(e.preferredLifetime())
166 << " valid:" << printable(e.validityLifetime());
167#undef printable
168 }
169 }
170 }
171}
172
173void tst_QNetworkInterface::consistencyCheck()
174{
175 QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
176 QSet<QString> interfaceNames;
177 QVector<int> interfaceIndexes;
178
179 foreach (const QNetworkInterface &iface, ifaces) {
180 QVERIFY(iface.isValid());
181 QVERIFY2(!interfaceNames.contains(iface.name()),
182 "duplicate name = " + iface.name().toLocal8Bit());
183 interfaceNames << iface.name();
184
185 QVERIFY2(!interfaceIndexes.contains(iface.index()),
186 "duplicate index = " + QByteArray::number(iface.index()));
187 if (iface.index())
188 interfaceIndexes << iface.index();
189
190 QVERIFY(iface.maximumTransmissionUnit() >= 0);
191
192 const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
193 for (auto entry : addresses) {
194 QVERIFY(entry.ip().protocol() != QAbstractSocket::UnknownNetworkLayerProtocol);
195 if (!entry.preferredLifetime().isForever() || !entry.validityLifetime().isForever())
196 QVERIFY(entry.isLifetimeKnown());
197 if (!entry.validityLifetime().isForever())
198 QVERIFY(entry.isTemporary());
199 }
200 }
201}
202
203void tst_QNetworkInterface::loopbackIPv4()
204{
205 QList<QHostAddress> all = QNetworkInterface::allAddresses();
206 QVERIFY(all.contains(QHostAddress(QHostAddress::LocalHost)));
207}
208
209void tst_QNetworkInterface::loopbackIPv6()
210{
211 if (!isIPv6Working())
212 QSKIP("IPv6 not active on this machine");
213 QList<QHostAddress> all = QNetworkInterface::allAddresses();
214 QVERIFY(all.contains(QHostAddress(QHostAddress::LocalHostIPv6)));
215}
216void tst_QNetworkInterface::localAddress_data()
217{
218 bool ipv6 = isIPv6Working();
219 QTest::addColumn<QHostAddress>(name: "target");
220
221 QTest::newRow(dataTag: "localhost-ipv4") << QHostAddress(QHostAddress::LocalHost);
222 if (ipv6)
223 QTest::newRow(dataTag: "localhost-ipv6") << QHostAddress(QHostAddress::LocalHostIPv6);
224
225 QTest::newRow(dataTag: "test-server") << QtNetworkSettings::serverIP();
226
227 QSet<QHostAddress> added;
228 const QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
229 for (const QNetworkInterface &iface : ifaces) {
230 if ((iface.flags() & QNetworkInterface::IsUp) == 0)
231 continue;
232 const QList<QNetworkAddressEntry> addrs = iface.addressEntries();
233 for (const QNetworkAddressEntry &entry : addrs) {
234 QHostAddress addr = entry.ip();
235 if (addr.isLoopback())
236 continue; // added above
237
238 if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
239 // add an IPv4 address with bits in the host portion of the address flipped
240 quint32 ip4 = entry.ip().toIPv4Address();
241 addr.setAddress(ip4 ^ ~entry.netmask().toIPv4Address());
242 } else if (!ipv6 || entry.prefixLength() != 64) {
243 continue;
244 } else {
245 // add a random node in this IPv6 network
246 quint64 randomid = qFromBigEndian(Q_UINT64_C(0x8f41f072e5733caa));
247 QIPv6Address ip6 = addr.toIPv6Address();
248 memcpy(dest: &ip6[8], src: &randomid, n: sizeof(randomid));
249 addr.setAddress(ip6);
250 }
251
252 if (added.contains(value: addr))
253 continue;
254 added.insert(value: addr);
255
256 QTest::addRow(format: "%s-%s", qPrintable(iface.name()), qPrintable(addr.toString())) << addr;
257 }
258 }
259}
260
261void tst_QNetworkInterface::localAddress()
262{
263 QFETCH(QHostAddress, target);
264 QUdpSocket socket;
265 socket.connectToHost(address: target, port: 80);
266 QVERIFY(socket.waitForConnected(5000));
267
268 QHostAddress local = socket.localAddress();
269
270 // find the interface that contains the address QUdpSocket reported
271 QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
272 const QNetworkInterface *outgoingIface = nullptr;
273 for (const QNetworkInterface &iface : ifaces) {
274 QList<QNetworkAddressEntry> addrs = iface.addressEntries();
275 for (const QNetworkAddressEntry &entry : addrs) {
276 if (entry.ip() == local) {
277 outgoingIface = &iface;
278 break;
279 }
280 }
281 if (outgoingIface)
282 break;
283 }
284 QVERIFY(outgoingIface);
285
286 // we get QVariant() if the QNativeSocketEngine doesn't know how to get the PMTU
287 int pmtu = socket.socketOption(option: QAbstractSocket::PathMtuSocketOption).toInt();
288 qDebug() << "Connected to" << target.toString() << "via interface" << outgoingIface->name()
289 << "pmtu" << pmtu;
290
291 // check that the Path MTU is less than or equal the interface's MTU
292 QVERIFY(pmtu <= outgoingIface->maximumTransmissionUnit());
293}
294
295void tst_QNetworkInterface::interfaceFromXXX_data()
296{
297 QTest::addColumn<QNetworkInterface>(name: "iface");
298
299 QList<QNetworkInterface> allInterfaces = QNetworkInterface::allInterfaces();
300 if (allInterfaces.count() == 0)
301 QSKIP("No interfaces to test!");
302 foreach (QNetworkInterface iface, allInterfaces)
303 QTest::newRow(dataTag: iface.name().toLocal8Bit()) << iface;
304}
305
306void tst_QNetworkInterface::interfaceFromXXX()
307{
308 QFETCH(QNetworkInterface, iface);
309
310 QVERIFY(QNetworkInterface::interfaceFromName(iface.name()).isValid());
311 if (int idx = iface.index()) {
312 QVERIFY(QNetworkInterface::interfaceFromIndex(idx).isValid());
313 if (EmulationDetector::isRunningArmOnX86())
314 QEXPECT_FAIL("", "SIOCGIFNAME fails on QEMU", Continue);
315 QCOMPARE(QNetworkInterface::interfaceNameFromIndex(idx), iface.name());
316 QCOMPARE(QNetworkInterface::interfaceIndexFromName(iface.name()), idx);
317 }
318 foreach (QNetworkAddressEntry entry, iface.addressEntries()) {
319 QVERIFY(!entry.ip().isNull());
320
321 if (!entry.netmask().isNull()) {
322 QCOMPARE(entry.netmask().protocol(), entry.ip().protocol());
323
324 // if the netmask is known, the broadcast is known
325 // but only for IPv4 (there is no such thing as broadcast in IPv6)
326 if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
327 QVERIFY(!entry.broadcast().isNull());
328 }
329 }
330
331 if (!entry.broadcast().isNull())
332 QCOMPARE(entry.broadcast().protocol(), entry.ip().protocol());
333 }
334}
335
336void tst_QNetworkInterface::copyInvalidInterface()
337{
338 // Force a copy of an interfaces that isn't likely to exist
339 QNetworkInterface i = QNetworkInterface::interfaceFromName(name: "plopp");
340 QVERIFY(!i.isValid());
341
342 QCOMPARE(i.index(), 0);
343 QVERIFY(i.name().isEmpty());
344 QVERIFY(i.humanReadableName().isEmpty());
345 QVERIFY(i.hardwareAddress().isEmpty());
346 QCOMPARE(int(i.flags()), 0);
347 QVERIFY(i.addressEntries().isEmpty());
348}
349
350QTEST_MAIN(tst_QNetworkInterface)
351#include "tst_qnetworkinterface.moc"
352

source code of qtbase/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp