Skip to content

Conversation

@jamilbk
Copy link
Member

@jamilbk jamilbk commented Dec 29, 2025

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.

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.
Copy link

Copilot AI left a 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();
Copy link

Copilot AI Dec 29, 2025

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.

Suggested change
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;
}

Copilot uses AI. Check for mistakes.
Comment on lines +498 to 500
let socket = match std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)) {
Ok(socket) => socket,
Err(_) => return 1,
Copy link

Copilot AI Dec 29, 2025

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.

Suggested change
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,
},

Copilot uses AI. Check for mistakes.

match rc {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
Copy link

Copilot AI Dec 29, 2025

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.

Suggested change
_ => 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),
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants