-
Notifications
You must be signed in to change notification settings - Fork 0
fix(windows): use WSASendMsg for GSO detection instead of setsockopt #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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(); |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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()), |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.