Skip to content

Commit

Permalink
Added FreeBSD's SCM_REALTIME and SCM_MONOTONIC into sys::socket::Cont…
Browse files Browse the repository at this point in the history
…rolMessageOwned (#2187)

* Added FreeBSD's SCM_REALTIME and SCM_MONOTONIC into sys::socket::ControlMessageOwned.

* Creating a SocketTimestamp enum for the SO_TS_CLOCK setsockopt for FreeBSD.

* Fixing whitespace

* Fixing CI error on cfg attributes

* Removing legacy doc attributes

* Formatting cleanup

* Updating changelog

* Adding tests for new TsClock setsockopt enum and the two new packet timestamp control messages for FreeBSD

* Replacing an assert_eq with an assert in new tests.

* Removing qemu ignore for new FreeBSD tests

* Giving new FreeBSD timestamping tests each a unique socket

* Updating monotonic packet timestamp test to account for monotonicity

* Moving test ports again to line up with changes in #2196

* Attempting checks again

* Wrapping ptr::read_unaligned calls in unsafe blocks
  • Loading branch information
recatek committed Nov 25, 2023
1 parent c1147c6 commit 3541bdd
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 2 deletions.
2 changes: 2 additions & 0 deletions changelog/2187.added.md
@@ -0,0 +1,2 @@
- Added the `::nix::sys::socket::SocketTimestamp` enum for configuring the `TsClock` (a.k.a `SO_TS_CLOCK`) sockopt
- Added FreeBSD's `ScmRealtime` and `ScmMonotonic` as new options in `::nix::sys::socket::ControlMessageOwned`
45 changes: 44 additions & 1 deletion src/sys/socket/mod.rs
@@ -1,7 +1,11 @@
//! Socket interface functions
//!
//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux"
))]
#[cfg(feature = "uio")]
use crate::sys::time::TimeSpec;
#[cfg(not(target_os = "redox"))]
Expand Down Expand Up @@ -380,6 +384,25 @@ libc_bitflags! {
}
}

#[cfg(target_os = "freebsd")]
libc_enum! {
/// A selector for which clock to use when generating packet timestamps.
/// Used when setting [`TsClock`](crate::sys::socket::sockopt::TsClock) on a socket.
/// (For more details, see [setsockopt(2)](https://man.freebsd.org/cgi/man.cgi?setsockopt)).
#[repr(i32)]
#[non_exhaustive]
pub enum SocketTimestamp {
/// Microsecond resolution, realtime. This is the default.
SO_TS_REALTIME_MICRO,
/// Sub-nanosecond resolution, realtime.
SO_TS_BINTIME,
/// Nanosecond resolution, realtime.
SO_TS_REALTIME,
/// Nanosecond resolution, monotonic.
SO_TS_MONOTONIC,
}
}

cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
/// Unix credentials of the sending process.
Expand Down Expand Up @@ -746,6 +769,16 @@ pub enum ControlMessageOwned {
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmTimestampns(TimeSpec),
/// Realtime clock timestamp
///
/// [Further reading](https://man.freebsd.org/cgi/man.cgi?setsockopt)
#[cfg(target_os = "freebsd")]
ScmRealtime(TimeSpec),
/// Monotonic clock timestamp
///
/// [Further reading](https://man.freebsd.org/cgi/man.cgi?setsockopt)
#[cfg(target_os = "freebsd")]
ScmMonotonic(TimeSpec),
#[cfg(any(
target_os = "android",
apple_targets,
Expand Down Expand Up @@ -926,6 +959,16 @@ impl ControlMessageOwned {
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
}
#[cfg(target_os = "freebsd")]
(libc::SOL_SOCKET, libc::SCM_REALTIME) => {
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmRealtime(TimeSpec::from(ts))
}
#[cfg(target_os = "freebsd")]
(libc::SOL_SOCKET, libc::SCM_MONOTONIC) => {
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmMonotonic(TimeSpec::from(ts))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
let tp = p as *const libc::timespec;
Expand Down
2 changes: 1 addition & 1 deletion src/sys/socket/sockopt.rs
Expand Up @@ -721,7 +721,7 @@ sockopt_impl!(
Both,
libc::SOL_SOCKET,
libc::SO_TS_CLOCK,
i32
super::SocketTimestamp
);
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
Expand Down
120 changes: 120 additions & 0 deletions test/sys/test_socket.rs
Expand Up @@ -72,6 +72,126 @@ pub fn test_timestamping() {
assert!(std::time::Duration::from(diff).as_secs() < 60);
}

#[cfg(target_os = "freebsd")]
#[test]
pub fn test_timestamping_realtime() {
use nix::sys::socket::{
recvmsg, sendmsg, setsockopt, socket, sockopt::ReceiveTimestamp,
sockopt::TsClock, ControlMessageOwned, MsgFlags, SockFlag, SockType,
SockaddrIn, SocketTimestamp,
};
use std::io::{IoSlice, IoSliceMut};

let sock_addr = SockaddrIn::from_str("127.0.0.1:6792").unwrap();

let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("send socket failed");

let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap();

setsockopt(&rsock, ReceiveTimestamp, &true).unwrap();
setsockopt(&rsock, TsClock, &SocketTimestamp::SO_TS_REALTIME).unwrap();

let sbuf = [0u8; 2048];
let mut rbuf = [0u8; 2048];
let flags = MsgFlags::empty();
let iov1 = [IoSlice::new(&sbuf)];
let mut iov2 = [IoSliceMut::new(&mut rbuf)];

let mut cmsg = cmsg_space!(nix::sys::time::TimeVal);
sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
let recv =
recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags)
.unwrap();

let mut ts = None;
for c in recv.cmsgs() {
if let ControlMessageOwned::ScmRealtime(timeval) = c {
ts = Some(timeval);
}
}
let ts = ts.expect("ScmRealtime is present");
let sys_time =
::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME)
.unwrap();
let diff = if ts > sys_time {
ts - sys_time
} else {
sys_time - ts
};
assert!(std::time::Duration::from(diff).as_secs() < 60);
}

#[cfg(target_os = "freebsd")]
#[test]
pub fn test_timestamping_monotonic() {
use nix::sys::socket::{
recvmsg, sendmsg, setsockopt, socket, sockopt::ReceiveTimestamp,
sockopt::TsClock, ControlMessageOwned, MsgFlags, SockFlag, SockType,
SockaddrIn, SocketTimestamp,
};
use std::io::{IoSlice, IoSliceMut};

let sock_addr = SockaddrIn::from_str("127.0.0.1:6803").unwrap();

let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("send socket failed");

let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap();

setsockopt(&rsock, ReceiveTimestamp, &true).unwrap();
setsockopt(&rsock, TsClock, &SocketTimestamp::SO_TS_MONOTONIC).unwrap();

let sbuf = [0u8; 2048];
let mut rbuf = [0u8; 2048];
let flags = MsgFlags::empty();
let iov1 = [IoSlice::new(&sbuf)];
let mut iov2 = [IoSliceMut::new(&mut rbuf)];

let mut cmsg = cmsg_space!(nix::sys::time::TimeVal);
sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
let recv =
recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags)
.unwrap();

let mut ts = None;
for c in recv.cmsgs() {
if let ControlMessageOwned::ScmMonotonic(timeval) = c {
ts = Some(timeval);
}
}
let ts = ts.expect("ScmMonotonic is present");
let sys_time =
::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_MONOTONIC)
.unwrap();
let diff = sys_time - ts; // Monotonic clock sys_time must be greater
assert!(std::time::Duration::from(diff).as_secs() < 60);
}

#[test]
pub fn test_path_to_sock_addr() {
let path = "/foo/bar";
Expand Down
113 changes: 113 additions & 0 deletions test/sys/test_sockopt.rs
Expand Up @@ -436,3 +436,116 @@ fn test_ipv6_tclass() {
setsockopt(&fd, sockopt::Ipv6TClass, &class).unwrap();
assert_eq!(getsockopt(&fd, sockopt::Ipv6TClass).unwrap(), class);
}

#[test]
#[cfg(target_os = "freebsd")]
fn test_receive_timestamp() {
let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();
assert!(getsockopt(&fd, sockopt::ReceiveTimestamp).unwrap());
}

#[test]
#[cfg(target_os = "freebsd")]
fn test_ts_clock_realtime_micro() {
use nix::sys::socket::SocketTimestamp;

let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();

// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();

setsockopt(
&fd,
sockopt::TsClock,
&SocketTimestamp::SO_TS_REALTIME_MICRO,
)
.unwrap();
assert_eq!(
getsockopt(&fd, sockopt::TsClock).unwrap(),
SocketTimestamp::SO_TS_REALTIME_MICRO
);
}

#[test]
#[cfg(target_os = "freebsd")]
fn test_ts_clock_bintime() {
use nix::sys::socket::SocketTimestamp;

let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();

// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();

setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_BINTIME).unwrap();
assert_eq!(
getsockopt(&fd, sockopt::TsClock).unwrap(),
SocketTimestamp::SO_TS_BINTIME
);
}

#[test]
#[cfg(target_os = "freebsd")]
fn test_ts_clock_realtime() {
use nix::sys::socket::SocketTimestamp;

let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();

// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();

setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_REALTIME)
.unwrap();
assert_eq!(
getsockopt(&fd, sockopt::TsClock).unwrap(),
SocketTimestamp::SO_TS_REALTIME
);
}

#[test]
#[cfg(target_os = "freebsd")]
fn test_ts_clock_monotonic() {
use nix::sys::socket::SocketTimestamp;

let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();

// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();

setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_MONOTONIC)
.unwrap();
assert_eq!(
getsockopt(&fd, sockopt::TsClock).unwrap(),
SocketTimestamp::SO_TS_MONOTONIC
);
}

0 comments on commit 3541bdd

Please sign in to comment.