summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-08-20 15:50:41 -0700
committerThiago Macieira <thiago.macieira@intel.com>2017-11-14 15:54:51 +0000
commitaaa187cd9951b71127c11c375e6a3954a187e1d3 (patch)
tree54dcee410994b3d64058ed6dde9e95c519fee83a
parentaa494d826ace02ffff5f29ae20b7738630913777 (diff)
QAbstractSocket: Add socketOption for the Path MTU
This allow retrieving the value of the known PMTU for the current socket. This works on Linux (IPv6 and IPv4) and FreeBSD (IPv6 only) -- the other OSes don't have the necessary API. Note: do we need add IP_MTU_DISCOVER? Change-Id: I6e9274c1e7444ad48c81fffd14dcaf97a18ce335 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--src/network/socket/qabstractsocket.cpp13
-rw-r--r--src/network/socket/qabstractsocket.h3
-rw-r--r--src/network/socket/qabstractsocketengine_p.h3
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp30
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp15
-rw-r--r--src/network/socket/qnativesocketengine_winrt.cpp2
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/BLACKLIST2
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp62
8 files changed, 118 insertions, 12 deletions
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 6d47540b75..98baa0c047 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -387,6 +387,11 @@
(see \l{QAbstractSocket::}{setReadBufferSize()}).
This enum value has been introduced in Qt 5.3.
+ \value PathMtuSocketOption Retrieves the Path Maximum Transmission Unit
+ (PMTU) value currently known by the IP stack, if any. Some IP stacks also
+ allow setting the MTU for transmission.
+ This enum value was introduced in Qt 5.11.
+
Possible values for \e{TypeOfServiceOption} are:
\table
@@ -2023,6 +2028,10 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons
case ReceiveBufferSizeSocketOption:
d_func()->socketEngine->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, value.toInt());
break;
+
+ case PathMtuSocketOption:
+ d_func()->socketEngine->setOption(QAbstractSocketEngine::PathMtuInformation, value.toInt());
+ break;
}
}
@@ -2065,6 +2074,10 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option)
case ReceiveBufferSizeSocketOption:
ret = d_func()->socketEngine->option(QAbstractSocketEngine::ReceiveBufferSocketOption);
break;
+
+ case PathMtuSocketOption:
+ ret = d_func()->socketEngine->option(QAbstractSocketEngine::PathMtuInformation);
+ break;
}
if (ret == -1)
return QVariant();
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index ba499ddf7d..6d5e57ac52 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -120,7 +120,8 @@ public:
MulticastLoopbackOption, // IP_MULTICAST_LOOPBACK
TypeOfServiceOption, //IP_TOS
SendBufferSizeSocketOption, //SO_SNDBUF
- ReceiveBufferSizeSocketOption //SO_RCVBUF
+ ReceiveBufferSizeSocketOption, //SO_RCVBUF
+ PathMtuSocketOption // IP_MTU
};
Q_ENUM(SocketOption)
enum BindFlag {
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index 0cb519ce90..b15dd73c96 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -105,7 +105,8 @@ public:
TypeOfServiceOption,
ReceivePacketInformation,
ReceiveHopLimit,
- MaxStreamsSocketOption
+ MaxStreamsSocketOption,
+ PathMtuInformation
};
enum PacketHeaderOption {
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index cb0a521360..b380b0f7d6 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -226,6 +226,20 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
#endif
}
break;
+
+ case QNativeSocketEngine::PathMtuInformation:
+ if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) {
+#ifdef IPV6_MTU
+ level = IPPROTO_IPV6;
+ n = IPV6_MTU;
+#endif
+ } else {
+#ifdef IP_MTU
+ level = IPPROTO_IP;
+ n = IP_MTU;
+#endif
+ }
+ break;
}
}
@@ -331,6 +345,20 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
return -1;
}
+ case QNativeSocketEngine::PathMtuInformation:
+#if defined(IPV6_PATHMTU) && !defined(IPV6_MTU)
+ // Prefer IPV6_MTU (handled by convertToLevelAndOption), if available
+ // (Linux); fall back to IPV6_PATHMTU otherwise (FreeBSD):
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ ip6_mtuinfo mtuinfo;
+ QT_SOCKOPTLEN_T len = sizeof(mtuinfo);
+ if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_PATHMTU, &mtuinfo, &len) == 0)
+ return int(mtuinfo.ip6m_mtu);
+ return -1;
+ }
+#endif
+ break;
+
default:
break;
}
@@ -420,6 +448,8 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
}
#endif
+ if (n == -1)
+ return false;
return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0;
}
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 1ec3df842f..fb31607e2c 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -209,7 +209,7 @@ static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt
static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
QAbstractSocket::NetworkLayerProtocol socketProtocol, int &level, int &n)
{
- n = 0;
+ n = -1;
level = SOL_SOCKET; // default
switch (opt) {
@@ -281,6 +281,9 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
n = IP_HOPLIMIT;
}
break;
+
+ case QAbstractSocketEngine::PathMtuInformation:
+ break; // not supported on Windows
}
}
@@ -471,9 +474,11 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
QT_SOCKOPTLEN_T len = sizeof(v);
convertToLevelAndOption(opt, socketProtocol, level, n);
- if (getsockopt(socketDescriptor, level, n, (char *) &v, &len) == 0)
- return v;
- WS_ERROR_DEBUG(WSAGetLastError());
+ if (n != -1) {
+ if (getsockopt(socketDescriptor, level, n, (char *) &v, &len) == 0)
+ return v;
+ WS_ERROR_DEBUG(WSAGetLastError());
+ }
return -1;
}
@@ -514,6 +519,8 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
int n, level;
convertToLevelAndOption(opt, socketProtocol, level, n);
+ if (n == -1)
+ return false;
if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) {
WS_ERROR_DEBUG(WSAGetLastError());
return false;
diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp
index 0baf5c9e21..9df5f0c500 100644
--- a/src/network/socket/qnativesocketengine_winrt.cpp
+++ b/src/network/socket/qnativesocketengine_winrt.cpp
@@ -1514,6 +1514,7 @@ int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt)
case QAbstractSocketEngine::MulticastLoopbackOption:
case QAbstractSocketEngine::TypeOfServiceOption:
case QAbstractSocketEngine::MaxStreamsSocketOption:
+ case QAbstractSocketEngine::PathMtuInformation:
default:
return -1;
}
@@ -1573,6 +1574,7 @@ bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption o
case QAbstractSocketEngine::MulticastLoopbackOption:
case QAbstractSocketEngine::TypeOfServiceOption:
case QAbstractSocketEngine::MaxStreamsSocketOption:
+ case QAbstractSocketEngine::PathMtuInformation:
default:
return false;
}
diff --git a/tests/auto/network/kernel/qnetworkinterface/BLACKLIST b/tests/auto/network/kernel/qnetworkinterface/BLACKLIST
deleted file mode 100644
index 23bb688d9a..0000000000
--- a/tests/auto/network/kernel/qnetworkinterface/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[localAddress]
-linux
diff --git a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp
index 93dd73d64a..0b4ed4870d 100644
--- a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp
+++ b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp
@@ -40,6 +40,8 @@
#include "../../../network-settings.h"
#include "emulationdetector.h"
+Q_DECLARE_METATYPE(QHostAddress)
+
class tst_QNetworkInterface : public QObject
{
Q_OBJECT
@@ -57,6 +59,7 @@ private slots:
void consistencyCheck();
void loopbackIPv4();
void loopbackIPv6();
+ void localAddress_data();
void localAddress();
void interfaceFromXXX_data();
void interfaceFromXXX();
@@ -210,18 +213,69 @@ void tst_QNetworkInterface::loopbackIPv6()
QList<QHostAddress> all = QNetworkInterface::allAddresses();
QVERIFY(all.contains(QHostAddress(QHostAddress::LocalHostIPv6)));
}
+void tst_QNetworkInterface::localAddress_data()
+{
+ QTest::addColumn<QHostAddress>("target");
+
+ QTest::newRow("localhost-ipv4") << QHostAddress(QHostAddress::LocalHost);
+ if (isIPv6Working())
+ 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");
+
+ 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())
+ continue;
+ QTest::addRow("linklocal-ipv6-%s", qPrintable(scope))
+ << QHostAddress("fe80::1234%" + scope);
+ }
+ }
+}
void tst_QNetworkInterface::localAddress()
{
+ QFETCH(QHostAddress, target);
QUdpSocket socket;
- socket.connectToHost(QtNetworkSettings::serverName(), 80);
+ socket.connectToHost(target, 80);
QVERIFY(socket.waitForConnected(5000));
QHostAddress local = socket.localAddress();
- // test that we can find the address that QUdpSocket reported
- QList<QHostAddress> all = QNetworkInterface::allAddresses();
- QVERIFY(all.contains(local));
+ // find the interface that contains the address QUdpSocket reported
+ QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
+ const QNetworkInterface *outgoingIface = nullptr;
+ for (const QNetworkInterface &iface : ifaces) {
+ QList<QNetworkAddressEntry> addrs = iface.addressEntries();
+ for (const QNetworkAddressEntry &entry : addrs) {
+ if (entry.ip() == local) {
+ outgoingIface = &iface;
+ break;
+ }
+ }
+ if (outgoingIface)
+ break;
+ }
+ QVERIFY(outgoingIface);
+
+ // we get QVariant() if the QNativeSocketEngine doesn't know how to get the PMTU
+ int pmtu = socket.socketOption(QAbstractSocket::PathMtuSocketOption).toInt();
+ qDebug() << "Connected to" << target.toString() << "via interface" << outgoingIface->name()
+ << "pmtu" << pmtu;
+
+ // check that the Path MTU is less than or equal the interface's MTU
+ QVERIFY(pmtu <= outgoingIface->maxTransmissionUnit());
}
void tst_QNetworkInterface::interfaceFromXXX_data()