From d23e7c7575f331fd2263f2f80b7698c49abe7907 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Mon, 6 Sep 2021 16:59:35 -0700 Subject: Add support for IP_RECVERR and IPV6_RECVERR Setting these options enables receiving errors, such as ICMP errors from the network, via `recvmsg()` with `MSG_ERRQUEUE`. Adds new `Ipv{4,6}RecvErr` variants to `ControlMessageOwned`. These control messages are produced when `Ipv4RecvErr` or `Ipv6RecvErr` options are enabled on a raw or datagram socket. New tests for the functionality can be run with `cargo test --test test test_recverr`. This commit builds on an earlier draft of the functionality authored by Matthew McPherrin . --- src/sys/socket/mod.rs | 35 +++++++++++++++++++++++++++++++++++ src/sys/socket/sockopt.rs | 4 ++++ 2 files changed, 39 insertions(+) (limited to 'src/sys/socket') diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 33585bb9..50d8b6fb 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -653,6 +653,13 @@ pub enum ControlMessageOwned { #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] RxqOvfl(u32), + /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag. + #[cfg(any(target_os = "android", target_os = "linux"))] + Ipv4RecvErr(libc::sock_extended_err, Option), + /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag. + #[cfg(any(target_os = "android", target_os = "linux"))] + Ipv6RecvErr(libc::sock_extended_err, Option), + /// Catch-all variant for unimplemented cmsg types. #[doc(hidden)] Unknown(UnknownCmsg), @@ -756,6 +763,16 @@ impl ControlMessageOwned { let drop_counter = ptr::read_unaligned(p as *const u32); ControlMessageOwned::RxqOvfl(drop_counter) }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::IPPROTO_IP, libc::IP_RECVERR) => { + let (err, addr) = Self::recv_err_helper::(p, len); + ControlMessageOwned::Ipv4RecvErr(err, addr) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => { + let (err, addr) = Self::recv_err_helper::(p, len); + ControlMessageOwned::Ipv6RecvErr(err, addr) + }, (_, _) => { let sl = slice::from_raw_parts(p, len); let ucmsg = UnknownCmsg(*header, Vec::::from(sl)); @@ -763,6 +780,24 @@ impl ControlMessageOwned { } } } + + #[cfg(any(target_os = "android", target_os = "linux"))] + unsafe fn recv_err_helper(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option) { + let ee = p as *const libc::sock_extended_err; + let err = ptr::read_unaligned(ee); + + // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len] + // CMSG_DATA buffer. For local errors, there is no address included in the control + // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to + // validate that the address object is in-bounds before we attempt to copy it. + let addrp = libc::SO_EE_OFFENDER(ee) as *const T; + + if addrp.offset(1) as usize - (p as usize) > len { + (err, None) + } else { + (err, Some(ptr::read_unaligned(addrp))) + } + } } /// A type-safe zero-copy wrapper around a single control message, as used wih diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index a937226f..195479b6 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -345,6 +345,10 @@ sockopt_impl!(Both, UdpGroSegment, libc::IPPROTO_UDP, libc::UDP_GRO, bool); #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] sockopt_impl!(Both, RxqOvfl, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int); sockopt_impl!(Both, Ipv6V6Only, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(Both, Ipv4RecvErr, libc::IPPROTO_IP, libc::IP_RECVERR, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(Both, Ipv6RecvErr, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool); #[cfg(any(target_os = "android", target_os = "linux"))] #[derive(Copy, Clone, Debug)] -- cgit v1.2.3