diff --git a/src/raw_socket.rs b/src/raw_socket.rs index 4ea442f..af60b1a 100644 --- a/src/raw_socket.rs +++ b/src/raw_socket.rs @@ -16,6 +16,8 @@ use crate::{ mod freebsd; #[cfg(target_os = "linux")] mod linux; +#[cfg(target_os = "macos")] +mod macos; // A struct providing safe wrappers around various socket api calls #[derive(Debug, Hash)] diff --git a/src/raw_socket/macos.rs b/src/raw_socket/macos.rs new file mode 100644 index 0000000..1ed6cb8 --- /dev/null +++ b/src/raw_socket/macos.rs @@ -0,0 +1,35 @@ +use crate::cerr; + +use super::RawSocket; + +impl RawSocket { + pub(crate) fn so_timestamp(&self, options: u32) -> std::io::Result<()> { + // Documentation on the timestamping calls: + // + // - linux: https://www.kernel.org/doc/Documentation/networking/timestamping.txt + // - freebsd: https://man.freebsd.org/cgi/man.cgi?setsockopt + // + // SAFETY: + // + // - the socket is provided by (safe) rust, and will outlive the call + // - method is guaranteed to be a valid "name" argument + // - the options pointer outlives the call + // - the `option_len` corresponds with the options pointer + // + // Only some bits are valid to set in `options`, but setting invalid bits is + // perfectly safe + // + // > Setting other bit returns EINVAL and does not change the current state. + unsafe { + cerr(libc::setsockopt( + self.fd, + libc::SOL_SOCKET, + libc::SO_TIMESTAMP, + &options as *const _ as *const libc::c_void, + std::mem::size_of_val(&options) as libc::socklen_t, + )) + }?; + + Ok(()) + } +} diff --git a/src/socket.rs b/src/socket.rs index 28988c7..3466ff0 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -9,19 +9,23 @@ use crate::{ raw_socket::RawSocket, }; -#[cfg(not(any(target_os = "linux", target_os = "freebsd")))] +#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))] mod fallback; #[cfg(target_os = "freebsd")] mod freebsd; #[cfg(target_os = "linux")] mod linux; +#[cfg(target_os = "macos")] +mod macos; -#[cfg(not(any(target_os = "linux", target_os = "freebsd")))] +#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))] use self::fallback::*; #[cfg(target_os = "freebsd")] use self::freebsd::*; #[cfg(target_os = "linux")] pub use self::linux::*; +#[cfg(target_os = "macos")] +use self::macos::*; #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Default)] pub struct Timestamp { @@ -30,7 +34,7 @@ pub struct Timestamp { } impl Timestamp { - #[cfg_attr(target_os = "macos", unused)] // macos does not do nanoseconds + #[cfg_attr(target_os = "macos", allow(unused))] // macos does not do nanoseconds pub(crate) fn from_timespec(timespec: libc::timespec) -> Self { Self { seconds: timespec.tv_sec as _, diff --git a/src/socket/macos.rs b/src/socket/macos.rs new file mode 100644 index 0000000..5175812 --- /dev/null +++ b/src/socket/macos.rs @@ -0,0 +1,14 @@ +use crate::raw_socket::RawSocket; + +use super::InterfaceTimestampMode; + +pub(super) fn configure_timestamping( + socket: &RawSocket, + mode: InterfaceTimestampMode, +) -> std::io::Result<()> { + match mode { + InterfaceTimestampMode::None => Ok(()), + InterfaceTimestampMode::SoftwareRecv => socket.so_timestamp(1), + _ => Err(std::io::ErrorKind::Unsupported.into()), + } +}