From a768129bb8d4542c7035a51af08546ccac5b07d6 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 29 Nov 2024 15:51:46 +0100 Subject: [PATCH] fix(udp): retry on ErrorKind::Interrupted On Linux and Android, `quinn-udp` will retry a send on `ErrorKind::Interrupted`: https://github.com/quinn-rs/quinn/blob/e318cc4a80436fd9fa19c02886d682c49efca185/quinn-udp/src/unix.rs#L305-L312 Do the same on all other unix platforms. --- quinn-udp/src/unix.rs | 77 +++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/quinn-udp/src/unix.rs b/quinn-udp/src/unix.rs index 1b3515297..a810912d7 100644 --- a/quinn-udp/src/unix.rs +++ b/quinn-udp/src/unix.rs @@ -383,31 +383,33 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io: hdrs[i].msg_datalen = chunk.len(); cnt += 1; } - let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) }; - if n >= 0 { - return Ok(()); - } - let e = io::Error::last_os_error(); - match e.kind() { - io::ErrorKind::Interrupted => { - // Retry the transmission - } - io::ErrorKind::WouldBlock => return Err(e), - _ => { - // Other errors are ignored, since they will usually be handled - // by higher level retransmits and timeouts. - // - PermissionDenied errors have been observed due to iptable rules. - // Those are not fatal errors, since the - // configuration can be dynamically changed. - // - Destination unreachable errors have been observed for other - // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid - // these by automatically clamping the MTUD upper bound to the interface MTU. - if e.raw_os_error() != Some(libc::EMSGSIZE) { - log_sendmsg_error(&state.last_send_error, e, transmit); + loop { + let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) }; + if n == -1 { + let e = io::Error::last_os_error(); + match e.kind() { + io::ErrorKind::Interrupted => { + // Retry the transmission + continue; + } + io::ErrorKind::WouldBlock => return Err(e), + _ => { + // Other errors are ignored, since they will usually be handled + // by higher level retransmits and timeouts. + // - PermissionDenied errors have been observed due to iptable rules. + // Those are not fatal errors, since the + // configuration can be dynamically changed. + // - Destination unreachable errors have been observed for other + // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid + // these by automatically clamping the MTUD upper bound to the interface MTU. + if e.raw_os_error() != Some(libc::EMSGSIZE) { + log_sendmsg_error(&state.last_send_error, e, transmit); + } + } } } + return Ok(()); } - Ok(()) } #[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))] @@ -425,24 +427,27 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io: cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"), state.sendmsg_einval(), ); - let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) }; - if n == -1 { - let e = io::Error::last_os_error(); - match e.kind() { - io::ErrorKind::Interrupted => { - // Retry the transmission - } - io::ErrorKind::WouldBlock => return Err(e), - _ => { - // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid - // these by automatically clamping the MTUD upper bound to the interface MTU. - if e.raw_os_error() != Some(libc::EMSGSIZE) { - return Err(e); + loop { + let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) }; + if n == -1 { + let e = io::Error::last_os_error(); + match e.kind() { + io::ErrorKind::Interrupted => { + // Retry the transmission + continue; + } + io::ErrorKind::WouldBlock => return Err(e), + _ => { + // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid + // these by automatically clamping the MTUD upper bound to the interface MTU. + if e.raw_os_error() != Some(libc::EMSGSIZE) { + return Err(e); + } } } } + return Ok(()); } - Ok(()) } #[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd", solarish)))]