From d372e74387bb7da70b78f33d020596430838d06d Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 12 Dec 2024 21:27:27 +0100 Subject: [PATCH] fix(udp): retry send on first EINVAL Android API level < 26 does not support the `libc::IP_TOS` control message. `sendmsg` calls with `libc::IP_TOS` return `libc::EINVAL`. https://github.com/quinn-rs/quinn/pull/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 https://github.com/quinn-rs/quinn/pull/1975. --- quinn-udp/src/unix.rs | 16 ++++++++++++++-- quinn-udp/tests/tests.rs | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/quinn-udp/src/unix.rs b/quinn-udp/src/unix.rs index 55bd21059..d5c779ed3 100644 --- a/quinn-udp/src/unix.rs +++ b/quinn-udp/src/unix.rs @@ -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 diff --git a/quinn-udp/tests/tests.rs b/quinn-udp/tests/tests.rs index e56d92fa2..7b0b48bda 100644 --- a/quinn-udp/tests/tests.rs +++ b/quinn-udp/tests/tests.rs @@ -241,7 +241,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::().ok())