diff options
| author | Ivan Solovev <ivan.solovev@qt.io> | 2025-09-22 11:47:14 +0200 |
|---|---|---|
| committer | Ivan Solovev <ivan.solovev@qt.io> | 2025-10-09 16:43:12 +0200 |
| commit | 83fc4ff9c20eb922113e08ba8566e077ce44dea3 (patch) | |
| tree | 61a399b1f23e06cba5d5fcb3e8eedbd30d3f1074 /src/network/socket/qnativesocketengine_win.cpp | |
| parent | e9778dfe6ead0c4d704570816a56aead084a0263 (diff) | |
Allow binding a socket to a specific QNetworkInterface
For now, via a private API.
Provide a new socket engine option - BindInterfaceIndex, and use it if
a valid interface is passed to QAbstractSocketPrivate::bind().
Note that this feature is not fully functional on macOS:
both SO_BINDTODEVICE and IP{V6}_BOUND_IF only work for limiting
outgoing datagrams. The patch uses IP{V6}_BOUND_IF, because it allows
to avoid number -> string -> number conversion.
As of now, extra filtering of incoming data should be done on the user
side.
Task-number: QTBUG-80704
Task-number: QTBUG-139697
Pick-to: 6.10 6.8
Change-Id: Ic207908313d9d25f96c23ecc363181ff0ae8232a
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/network/socket/qnativesocketengine_win.cpp')
| -rw-r--r-- | src/network/socket/qnativesocketengine_win.cpp | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 9933ea06745..598467ef629 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -153,6 +153,9 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, case QNativeSocketEngine::MaxStreamsSocketOption: Q_UNREACHABLE(); + case QNativeSocketEngine::BindInterfaceIndex: + Q_UNREACHABLE(); // handled directly in setOption() + case QNativeSocketEngine::ReceiveBufferSocketOption: n = SO_RCVBUF; break; @@ -441,6 +444,64 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt return true; } + case QNativeSocketEngine::BindInterfaceIndex: { + int ret = 0; + if (socketProtocol == QAbstractSocket::IPv6Protocol + || socketProtocol == QAbstractSocket::AnyIPProtocol) { + // IPv6 - uses host byte order + // Bind outgoing datagrams to the interface + if (!ret) { + ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_UNICAST_IF, + reinterpret_cast<char *>(&v), sizeof(v)); + } + if (!ret) { + ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast<char *>(&v), sizeof(v)); + } + // Bind incoming datagrams to the interface + if (!ret) { + const int enable = 1; + ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_IFLIST, + reinterpret_cast<const char *>(&enable), sizeof(enable)); + if (!ret) { + ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_ADD_IFLIST, + reinterpret_cast<char *>(&v), sizeof(v)); + } + } + } + bool result = !ret; + if (result) { + // Try to set the IPv4 options unconditionally, but ignore + // the result if the protocol is not IPv4-only + + // IPv4 - uses network byte order + int netIdx = htonl(v); + // Bind outgoing datagrams to the interface + if (!ret) { + ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_UNICAST_IF, + reinterpret_cast<char *>(&netIdx), sizeof(netIdx)); + } + if (!ret) { + ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, + reinterpret_cast<char *>(&netIdx), sizeof(netIdx)); + } + // Bind incoming datagrams to the interface + if (!ret) { + const int enable = 1; + ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_IFLIST, + reinterpret_cast<const char *>(&enable), sizeof(enable)); + if (!ret) { + // uses host byte order here + ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_ADD_IFLIST, + reinterpret_cast<char *>(&v), sizeof(v)); + } + } + if (socketProtocol == QAbstractSocket::IPv4Protocol) + result = !ret; + } + return result; + } + default: break; } |
