Skip to content

Commit

Permalink
fix(udp): retry send on first EINVAL
Browse files Browse the repository at this point in the history
Android API level < 26 does not support the `libc::IP_TOS` control message.
`sendmsg` calls with `libc::IP_TOS` return `libc::EINVAL`.

quinn-rs#1516 added a fallback, not setting
`libc::IP_TOS` on consecutive calls to `sendmsg` after a failure with
`libc::EINVAL`. The current datagram would be dropped. Consecutive datagrams
passed to `sendmsg` would succeed as they would be sent without `libc::IP_TOS`
through the fallback.

Instead of dropping the first datagram on `libc::EINVAL`, this commit adds a
retry for it without `libc::IP_TOS`.

This is e.g. relevant for Neqo. When establishing a QUIC connection, dropping
the first datagram [delays connection establishment by
100ms](https://github.com/mozilla/neqo/blob/3001a3a56f2274eaafaa956fb394f0817f526ae7/neqo-transport/src/rtt.rs#L28).
With the retry introduced in this commit, delay due to unsupported
`libc::IP_TOS` should be negligeable.

Closes quinn-rs#1975.
  • Loading branch information
mxinden committed Dec 13, 2024
1 parent 12a5db9 commit 148739d
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 3 deletions.
16 changes: 14 additions & 2 deletions quinn-udp/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,20 @@ fn send(

if e.raw_os_error() == Some(libc::EINVAL) {
// Some arguments to `sendmsg` are not supported.
// Switch to fallback mode.
state.set_sendmsg_einval();
if !state.sendmsg_einval() {
// Switch to fallback mode.
state.set_sendmsg_einval();
prepare_msg(
transmit,
&dst_addr,
&mut msg_hdr,
&mut iovec,
&mut cmsgs,
encode_src_ip,
state.sendmsg_einval(),
);
continue;
}
}

// - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
Expand Down
5 changes: 4 additions & 1 deletion quinn-udp/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,10 @@ fn test_send_recv(send: &Socket, recv: &Socket, transmit: Transmit) {
}
}

if cfg!(target_os = "android")
if match transmit.destination.ip() {
IpAddr::V4(_) => true,
IpAddr::V6(a) => a.to_ipv4_mapped().is_some(),
} && cfg!(target_os = "android")
&& std::env::var("API_LEVEL")
.ok()
.and_then(|v| v.parse::<u32>().ok())
Expand Down

0 comments on commit 148739d

Please sign in to comment.