Skip to content

Commit

Permalink
Support netbsd
Browse files Browse the repository at this point in the history
NetBSD does not support DONTFRAG or ECN on IPv4, but does for IPv6.
It has recvmmsg.
  • Loading branch information
flub authored and Ralith committed Jun 2, 2024
1 parent b55ec29 commit 72ef0c0
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 24 deletions.
4 changes: 4 additions & 0 deletions quinn-udp/src/cmsg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ pub(crate) trait MsgHdr {

fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage;

/// Sets the number of control messages added to this `struct msghdr`.
///
/// Note that this is a destructive operation and should only be done as a finalisation
/// step.
fn set_control_len(&mut self, len: usize);

fn control_len(&self) -> usize;
Expand Down
5 changes: 5 additions & 0 deletions quinn-udp/src/cmsg/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ impl MsgHdr for libc::msghdr {

fn set_control_len(&mut self, len: usize) {
self.msg_controllen = len as _;
if len == 0 {
// netbsd is particular about this being a NULL pointer if there are no control
// messages.
self.msg_control = std::ptr::null_mut();
}
}

fn control_len(&self) -> usize {
Expand Down
3 changes: 2 additions & 1 deletion quinn-udp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ pub struct RecvMeta {
pub ecn: Option<EcnCodepoint>,
/// The destination IP address which was encoded in this datagram
///
/// Populated on platforms: Windows, Linux, Android, FreeBSD, OpenBSD, macOS, and iOS.
/// Populated on platforms: Windows, Linux, Android, FreeBSD, OpenBSD, NetBSD, macOS,
/// and iOS.
pub dst_ip: Option<IpAddr>,
}

Expand Down
64 changes: 45 additions & 19 deletions quinn-udp/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ use super::{
// Defined in netinet6/in6.h on OpenBSD, this is not yet exported by the libc crate
// directly. See https://github.com/rust-lang/libc/issues/3704 for when we might be able to
// rely on this from the libc crate.
#[cfg(target_os = "openbsd")]
#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
const IPV6_DONTFRAG: libc::c_int = 62;
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
const IPV6_DONTFRAG: libc::c_int = libc::IPV6_DONTFRAG;

#[cfg(target_os = "freebsd")]
type IpTosTy = libc::c_uchar;
#[cfg(not(target_os = "freebsd"))]
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
type IpTosTy = libc::c_int;

/// Tokio-compatible UDP socket with some useful specializations.
Expand Down Expand Up @@ -57,6 +57,7 @@ impl UdpSocketState {
if cfg!(target_os = "linux")
|| cfg!(target_os = "freebsd")
|| cfg!(target_os = "openbsd")
|| cfg!(target_os = "netbsd")
|| cfg!(target_os = "macos")
|| cfg!(target_os = "ios")
|| cfg!(target_os = "android")
Expand All @@ -82,7 +83,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"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
if is_ipv4 || !io.only_v6()? {
if let Err(err) = set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
{
Expand Down Expand Up @@ -120,7 +121,7 @@ impl UdpSocketState {
}
#[cfg(any(
target_os = "freebsd",
target_od = "openbsd",
target_os = "openbsd",
target_os = "macos",
target_os = "ios"
))]
Expand All @@ -138,6 +139,7 @@ impl UdpSocketState {
#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "macos",
target_os = "ios"
))]
Expand Down Expand Up @@ -218,13 +220,23 @@ impl UdpSocketState {
}

/// Sets the flag indicating we got EINVAL error from `sendmsg` or `sendmmsg` syscall.
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))]
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "openbsd",
target_os = "netbsd"
)))]
fn set_sendmsg_einval(&self) {
self.sendmsg_einval.store(true, Ordering::Relaxed)
}
}

#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "openbsd")))]
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "openbsd",
target_os = "netbsd"
)))]
fn send(
#[allow(unused_variables)] // only used on Linux
state: &UdpSocketState,
Expand Down Expand Up @@ -309,7 +321,12 @@ fn send(
}
}

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "openbsd",
target_os = "netbsd"
))]
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() };
Expand All @@ -321,8 +338,10 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io:
&mut hdr,
&mut iov,
&mut ctrl,
// Only tested on macOS and iOS
cfg!(target_os = "macos") || cfg!(target_os = "ios") || cfg!(target_os = "openbsd"),
cfg!(target_os = "macos")
|| cfg!(target_os = "ios")
|| cfg!(target_os = "openbsd")
|| cfg!(target_os = "netbsd"),
state.sendmsg_einval(),
);
let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
Expand Down Expand Up @@ -426,7 +445,7 @@ unsafe fn recvmmsg_with_fallback(
let flags = 0;
let timeout = ptr::null_mut::<libc::timespec>();

#[cfg(not(target_os = "freebsd"))]
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
{
let ret =
libc::syscall(libc::SYS_recvmmsg, sockfd, msgvec, vlen, flags, timeout) as libc::c_int;
Expand All @@ -435,11 +454,13 @@ unsafe fn recvmmsg_with_fallback(
}
}

// libc on FreeBSD implements `recvmmsg` as a high-level abstraction over `recvmsg`,
// thus `SYS_recvmmsg` constant and direct system call do not exist
#[cfg(target_os = "freebsd")]
// libc on FreeBSD and NetBSD implement `recvmmsg` as a high-level abstraction over
// `recvmsg`, thus `SYS_recvmmsg` constant and direct system call do not exist
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
{
let ret = libc::recvmmsg(sockfd, msgvec, vlen as usize, flags, timeout) as libc::c_int;
#[cfg(target_os = "freebsd")]
let vlen = vlen as usize;
let ret = libc::recvmmsg(sockfd, msgvec, vlen, flags, timeout) as libc::c_int;
if ret != -1 {
return ret;
}
Expand Down Expand Up @@ -516,7 +537,10 @@ fn prepare_msg(
|| matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
if is_ipv4 {
if !sendmsg_einval {
encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
#[cfg(not(target_os = "netbsd"))]
{
encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
}
}
} else {
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
Expand All @@ -542,9 +566,10 @@ fn prepare_msg(
}
#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "macos",
target_os = "ios",
target_os = "openbsd"
))]
{
if encode_src_ip {
Expand Down Expand Up @@ -603,7 +628,7 @@ fn decode_recv(
ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
},
// FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
(libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe {
ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
},
Expand All @@ -628,9 +653,10 @@ fn decode_recv(
}
#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "macos",
target_os = "ios",
target_os = "openbsd"
))]
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
let in_addr = unsafe { cmsg::decode::<libc::in_addr, libc::cmsghdr>(cmsg) };
Expand Down
8 changes: 4 additions & 4 deletions quinn-udp/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::{
io::IoSliceMut,
Expand Down Expand Up @@ -51,7 +51,7 @@ fn ecn_v6() {
}

#[test]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
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());
Expand All @@ -71,7 +71,7 @@ fn ecn_v4() {
}

#[test]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
fn ecn_v6_dualstack() {
let recv = socket2::Socket::new(
socket2::Domain::IPV6,
Expand Down Expand Up @@ -114,7 +114,7 @@ fn ecn_v6_dualstack() {
}

#[test]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
fn ecn_v4_mapped_v6() {
let send = socket2::Socket::new(
socket2::Domain::IPV6,
Expand Down
1 change: 1 addition & 0 deletions quinn/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ fn run_echo(args: EchoArgs) {
if cfg!(target_os = "linux")
|| cfg!(target_os = "freebsd")
|| cfg!(target_os = "openbsd")
|| cfg!(target_os = "netbsd")
|| cfg!(target_os = "macos")
|| cfg!(target_os = "windows")
{
Expand Down

0 comments on commit 72ef0c0

Please sign in to comment.