fix(windows): use WSASendMsg for GSO detection instead of setsockopt#1
fix(windows): use WSASendMsg for GSO detection instead of setsockopt#1
Conversation
The previous GSO detection used setsockopt() to test if UDP_SEND_MSG_SIZE was supported. However, this can succeed even when the actual WSASendMsg() call with the control message fails (e.g., due to network adapter/driver limitations). This changes the detection to actually try sending a test packet with the GSO control message, which more accurately reflects whether GSO will work at runtime. Fixes WSAEINVAL (error 10022) on some Windows configurations when sending UDP packets with the segment size control message.
There was a problem hiding this comment.
Pull request overview
This PR fixes GSO (Generic Send Offload) detection on Windows by replacing the setsockopt() test with an actual WSASendMsg call using a GSO control message. The previous approach could incorrectly report GSO support when the network adapter or driver doesn't actually support it, leading to WSAEINVAL errors at runtime.
Key Changes:
- Replace setsockopt-based GSO detection with a test packet send using WSASendMsg
- Add
send_test_packet()helper function that sends a test UDP packet with UDP_SEND_MSG_SIZE control message - Simplify socket binding to IPv4-only during detection
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Empirically found on Windows 11 x64 | ||
| Ok(()) => 512, | ||
|
|
||
| socket.set_nonblocking(true).ok(); |
There was a problem hiding this comment.
Errors from set_nonblocking(true) are silently ignored with .ok(). If this call fails, the socket remains in blocking mode, which could cause the subsequent send_test_packet to block indefinitely if the discard service isn't responding. Consider propagating this error or at minimum handling it explicitly, e.g., by returning 1 (no GSO) if nonblocking mode cannot be enabled.
| socket.set_nonblocking(true).ok(); | |
| if let Err(e) = socket.set_nonblocking(true) { | |
| debug!( | |
| "failed to set nonblocking mode for GSO detection socket: {}", | |
| e | |
| ); | |
| return 1; | |
| } |
| let socket = match std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)) { | ||
| Ok(socket) => socket, | ||
| Err(_) => return 1, |
There was a problem hiding this comment.
The socket binding was changed from attempting IPv6 first with IPv4 fallback to only IPv4. This change could miss detecting IPv6-specific GSO support or fail on systems where IPv4 binding is unavailable. Consider restoring the original fallback pattern to maintain broader compatibility and more comprehensive GSO detection.
| let socket = match std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)) { | |
| Ok(socket) => socket, | |
| Err(_) => return 1, | |
| let socket = match std::net::UdpSocket::bind((std::net::Ipv6Addr::LOCALHOST, 0)) { | |
| Ok(socket) => socket, | |
| Err(_) => match std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)) { | |
| Ok(socket) => socket, | |
| Err(_) => return 1, | |
| }, |
|
|
||
| match rc { | ||
| 0 => Ok(()), | ||
| _ => Err(io::Error::last_os_error()), |
There was a problem hiding this comment.
When the socket is in nonblocking mode and the send operation cannot complete immediately, WSASendMsg returns a non-zero value with the error WSAEWOULDBLOCK (error code 10035). This error should not be interpreted as GSO being unsupported. Consider distinguishing between WSAEWOULDBLOCK (which should be treated as success for detection purposes) and actual GSO-related errors like WSAEINVAL.
| _ => Err(io::Error::last_os_error()), | |
| _ => { | |
| let err = io::Error::last_os_error(); | |
| // When the socket is nonblocking, WSAEWOULDBLOCK means the send cannot | |
| // complete immediately, not that GSO is unsupported. Treat this as | |
| // success for detection purposes. | |
| match err.raw_os_error() { | |
| Some(code) if code == WinSock::WSAEWOULDBLOCK as i32 => Ok(()), | |
| _ => Err(err), | |
| } | |
| } |
The previous GSO detection used setsockopt() to test if UDP_SEND_MSG_SIZE was supported. However, this can succeed even when the actual WSASendMsg() call with the control message fails (e.g., due to network adapter/driver limitations).
This changes the detection to actually try sending a test packet with the GSO control message, which more accurately reflects whether GSO will work at runtime.
Fixes WSAEINVAL (error 10022) on some Windows configurations when sending UDP packets with the segment size control message.