From f4438c51fc7d5f717fce2e3eede124ade6cbabad Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Wed, 15 May 2024 13:39:51 +0200 Subject: [PATCH] Support openbsd OpenBSD supports ECN on IPv6 but not on IPv4 it seems. This splits off the test cases so that IPv6 ECN support is tested in isolation. --- quinn-udp/src/unix.rs | 74 +++++++++++++++++++++++++++++----------- quinn-udp/tests/tests.rs | 64 +++++++++++++++++++++++----------- quinn/src/tests.rs | 1 + 3 files changed, 99 insertions(+), 40 deletions(-) diff --git a/quinn-udp/src/unix.rs b/quinn-udp/src/unix.rs index d4d6fb129a..7bf9f3c44c 100644 --- a/quinn-udp/src/unix.rs +++ b/quinn-udp/src/unix.rs @@ -1,4 +1,4 @@ -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))] use std::ptr; use std::{ io::{self, IoSliceMut}, @@ -48,6 +48,7 @@ impl UdpSocketState { let mut cmsg_platform_space = 0; if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") + || cfg!(target_os = "openbsd") || cfg!(target_os = "macos") || cfg!(target_os = "ios") || cfg!(target_os = "android") @@ -73,6 +74,7 @@ impl UdpSocketState { // mac and ios do not support IP_RECVTOS on dual-stack sockets :( // older macos versions also don't have the flag and will error out if we don't ignore it + #[cfg(not(target_os = "openbsd"))] if is_ipv4 || !io.only_v6()? { if let Err(err) = set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON) { @@ -108,7 +110,12 @@ impl UdpSocketState { )?; } } - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))] + #[cfg(any( + target_os = "freebsd", + target_od = "openbsd", + target_os = "macos", + target_os = "ios" + ))] { if is_ipv4 { // Set `may_fragment` to `true` if this option is not supported on the platform. @@ -120,7 +127,12 @@ impl UdpSocketState { )?; } } - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))] + #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios" + ))] // IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD // macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS // macOS also supports IP_PKTINFO @@ -138,12 +150,19 @@ impl UdpSocketState { // kernel's path MTU guess, but actually disabling fragmentation requires this too. See // __ip6_append_data in ip6_output.c. // Set `may_fragment` to `true` if this option is not supported on the platform. - may_fragment |= !set_socket_option_supported( - &*io, - libc::IPPROTO_IPV6, - libc::IPV6_DONTFRAG, - OPTION_ON, - )?; + #[cfg(not(target_os = "openbsd"))] + { + may_fragment |= !set_socket_option_supported( + &*io, + libc::IPPROTO_IPV6, + libc::IPV6_DONTFRAG, + OPTION_ON, + )?; + } + #[cfg(target_os = "openbsd")] + { + may_fragment |= true; + } } let now = Instant::now(); @@ -202,13 +221,13 @@ impl UdpSocketState { } /// Sets the flag indicating we got EINVAL error from `sendmsg` or `sendmmsg` syscall. - #[cfg(not(any(target_os = "macos", target_os = "ios")))] + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))] fn set_sendmsg_einval(&self) { self.sendmsg_einval.store(true, Ordering::Relaxed) } } -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))] fn send( #[allow(unused_variables)] // only used on Linux state: &UdpSocketState, @@ -293,7 +312,7 @@ fn send( } } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))] fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> { let mut hdr: libc::msghdr = unsafe { mem::zeroed() }; let mut iov: libc::iovec = unsafe { mem::zeroed() }; @@ -306,7 +325,7 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io: &mut iov, &mut ctrl, // Only tested on macOS and iOS - cfg!(target_os = "macos") || cfg!(target_os = "ios"), + cfg!(target_os = "macos") || cfg!(target_os = "ios") || cfg!(target_os = "openbsd"), state.sendmsg_einval(), ); let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) }; @@ -335,7 +354,7 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io: Ok(()) } -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))] fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result { let mut names = [MaybeUninit::::uninit(); BATCH_SIZE]; let mut ctrls = [cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); BATCH_SIZE]; @@ -372,7 +391,7 @@ fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> Ok(msg_count as usize) } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))] fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result { let mut name = MaybeUninit::::uninit(); let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); @@ -401,7 +420,7 @@ fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> /// /// It uses [`libc::syscall`] instead of [`libc::recvmmsg`] /// to avoid linking error on systems where libc does not contain `recvmmsg`. -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))] unsafe fn recvmmsg_with_fallback( sockfd: libc::c_int, msgvec: *mut libc::mmsghdr, @@ -442,7 +461,7 @@ unsafe fn recvmmsg_with_fallback( /// Fallback implementation of `recvmmsg` using `recvmsg` /// for systems which do not support `recvmmsg` /// such as Linux <2.6.33. -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))] unsafe fn recvmmsg_fallback( sockfd: libc::c_int, msgvec: *mut libc::mmsghdr, @@ -524,7 +543,12 @@ fn prepare_msg( }; encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo); } - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))] + #[cfg(any( + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + target_os = "openbsd" + ))] { if encode_src_ip { let addr = libc::in_addr { @@ -579,7 +603,11 @@ fn decode_recv( for cmsg in cmsg_iter { match (cmsg.cmsg_level, cmsg.cmsg_type) { // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in. - (libc::IPPROTO_IP, libc::IP_TOS) | (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe { + (libc::IPPROTO_IP, libc::IP_TOS) => unsafe { + ecn_bits = cmsg::decode::(cmsg); + }, + #[cfg(not(target_os = "openbsd"))] + (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe { ecn_bits = cmsg::decode::(cmsg); }, (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe { @@ -601,7 +629,12 @@ fn decode_recv( pktinfo.ipi_addr.s_addr.to_ne_bytes(), ))); } - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))] + #[cfg(any( + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + target_os = "openbsd" + ))] (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => { let in_addr = unsafe { cmsg::decode::(cmsg) }; dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()))); @@ -730,6 +763,7 @@ mod gro { /// /// Yields `Ok(true)` if the option was set successfully, `Ok(false)` if setting /// the option raised an `ENOPROTOOPT` error, and `Err` for any other error. +#[cfg(not(target_os = "openbsd"))] fn set_socket_option_supported( socket: &impl AsRawFd, level: libc::c_int, diff --git a/quinn-udp/tests/tests.rs b/quinn-udp/tests/tests.rs index c656285b7f..adb84a6918 100644 --- a/quinn-udp/tests/tests.rs +++ b/quinn-udp/tests/tests.rs @@ -1,6 +1,8 @@ +#[cfg(not(target_os = "openbsd"))] +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::{ io::IoSliceMut, - net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket}, slice, }; @@ -31,6 +33,46 @@ fn basic() { #[test] fn ecn_v6() { + let send = Socket::from(UdpSocket::bind("[::1]:0").unwrap()); + let recv = Socket::from(UdpSocket::bind("[::1]:0").unwrap()); + for codepoint in [EcnCodepoint::Ect0, EcnCodepoint::Ect1] { + test_send_recv( + &send, + &recv, + Transmit { + destination: recv.local_addr().unwrap().as_socket().unwrap(), + ecn: Some(codepoint), + contents: b"hello", + segment_size: None, + src_ip: None, + }, + ); + } +} + +#[test] +#[cfg(not(target_os = "openbsd"))] +fn ecn_v4() { + let send = Socket::from(UdpSocket::bind("127.0.0.1:0").unwrap()); + let recv = Socket::from(UdpSocket::bind("127.0.0.1:0").unwrap()); + for codepoint in [EcnCodepoint::Ect0, EcnCodepoint::Ect1] { + test_send_recv( + &send, + &recv, + Transmit { + destination: recv.local_addr().unwrap().as_socket().unwrap(), + ecn: Some(codepoint), + contents: b"hello", + segment_size: None, + src_ip: None, + }, + ); + } +} + +#[test] +#[cfg(not(target_os = "openbsd"))] +fn enc_v6_dualstack() { let recv = socket2::Socket::new( socket2::Domain::IPV6, socket2::Type::DGRAM, @@ -72,25 +114,7 @@ fn ecn_v6() { } #[test] -fn ecn_v4() { - let send = Socket::from(UdpSocket::bind("127.0.0.1:0").unwrap()); - let recv = Socket::from(UdpSocket::bind("127.0.0.1:0").unwrap()); - for codepoint in [EcnCodepoint::Ect0, EcnCodepoint::Ect1] { - test_send_recv( - &send, - &recv, - Transmit { - destination: recv.local_addr().unwrap().as_socket().unwrap(), - ecn: Some(codepoint), - contents: b"hello", - segment_size: None, - src_ip: None, - }, - ); - } -} - -#[test] +#[cfg(not(target_os = "openbsd"))] fn ecn_v4_mapped_v6() { let send = socket2::Socket::new( socket2::Domain::IPV6, diff --git a/quinn/src/tests.rs b/quinn/src/tests.rs index e51157aa3d..690550773c 100755 --- a/quinn/src/tests.rs +++ b/quinn/src/tests.rs @@ -532,6 +532,7 @@ fn run_echo(args: EchoArgs) { // platforms in the doc comment of `quinn_udp::RecvMeta::dst_ip`. if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") + || cfg!(target_os = "openbsd") || cfg!(target_os = "macos") || cfg!(target_os = "windows") {