Skip to content
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

Fallback to not using ECN if IP_TOS is not supported #1516

Merged
merged 2 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion quinn-udp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::os::unix::io::AsRawFd;
use std::os::windows::io::AsRawSocket;
use std::{
net::{IpAddr, Ipv6Addr, SocketAddr},
sync::atomic::{AtomicUsize, Ordering},
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
time::{Duration, Instant},
};

Expand Down Expand Up @@ -37,6 +37,14 @@ pub const BATCH_SIZE: usize = imp::BATCH_SIZE;
pub struct UdpState {
max_gso_segments: AtomicUsize,
gro_segments: usize,

/// True if we have received EINVAL error from `sendmsg` or `sendmmsg` system call at least once.
///
/// If enabled, we assume that old kernel is used and switch to fallback mode.
/// In particular, we do not use IP_TOS cmsg_type in this case,
/// which is not supported on Linux <3.13 and results in not sending the UDP packet at all.
#[cfg(not(windows))]
sendmsg_einval: AtomicBool,
}

impl UdpState {
Expand All @@ -62,6 +70,20 @@ impl UdpState {
pub fn gro_segments(&self) -> usize {
self.gro_segments
}

/// Returns true if we previously got an EINVAL error from `sendmsg` or `sendmmsg` syscall.
#[inline]
#[cfg(not(windows))]
pub fn sendmsg_einval(&self) -> bool {
self.sendmsg_einval.load(Ordering::Relaxed)
}

/// Sets the flag indicating we got EINVAL error from `sendmsg` or `sendmmsg` syscall.
#[inline]
#[cfg(not(windows))]
pub fn set_sendmsg_einval(&self) {
self.sendmsg_einval.store(true, Ordering::Relaxed)
}
}

impl Default for UdpState {
Expand Down
18 changes: 15 additions & 3 deletions quinn-udp/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
mem::{self, MaybeUninit},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
os::unix::io::AsRawFd,
sync::atomic::AtomicUsize,
sync::atomic::{AtomicBool, AtomicUsize},
time::Instant,
};

Expand Down Expand Up @@ -189,6 +189,7 @@ fn send(
&mut iovecs[i],
&mut cmsgs[i],
encode_src_ip,
state.sendmsg_einval(),
);
}
let num_transmits = transmits.len().min(BATCH_SIZE);
Expand Down Expand Up @@ -221,6 +222,12 @@ 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();
}

// Other errors are ignored, since they will ususally be handled
// by higher level retransmits and timeouts.
// - PermissionDenied errors have been observed due to iptable rules.
Expand All @@ -243,7 +250,7 @@ fn send(

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn send(
_state: &UdpState,
state: &UdpState,
io: SockRef<'_>,
last_send_error: &mut Instant,
transmits: &[Transmit],
Expand All @@ -263,6 +270,7 @@ fn send(
&mut ctrl,
// Only tested on macOS
cfg!(target_os = "macos"),
state.sendmsg_einval(),
);
let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
if n == -1 {
Expand Down Expand Up @@ -459,6 +467,7 @@ pub fn udp_state() -> UdpState {
UdpState {
max_gso_segments: AtomicUsize::new(gso::max_gso_segments()),
gro_segments: gro::gro_segments(),
sendmsg_einval: AtomicBool::new(false),
}
}

Expand All @@ -472,6 +481,7 @@ fn prepare_msg(
ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
#[allow(unused_variables)] // only used on FreeBSD & macOS
encode_src_ip: bool,
sendmsg_einval: bool,
) {
iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
iov.iov_len = transmit.contents.len();
Expand All @@ -493,7 +503,9 @@ fn prepare_msg(
let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
if transmit.destination.is_ipv4() {
encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
if !sendmsg_einval {
encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
}
} else {
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
}
Expand Down