Skip to content

Commit 3541bdd

Browse files
authored
Added FreeBSD's SCM_REALTIME and SCM_MONOTONIC into sys::socket::ControlMessageOwned (#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
1 parent c1147c6 commit 3541bdd

File tree

5 files changed

+280
-2
lines changed

5 files changed

+280
-2
lines changed

changelog/2187.added.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Added the `::nix::sys::socket::SocketTimestamp` enum for configuring the `TsClock` (a.k.a `SO_TS_CLOCK`) sockopt
2+
- Added FreeBSD's `ScmRealtime` and `ScmMonotonic` as new options in `::nix::sys::socket::ControlMessageOwned`

src/sys/socket/mod.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
//! Socket interface functions
22
//!
33
//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
4-
#[cfg(any(target_os = "android", target_os = "linux"))]
4+
#[cfg(any(
5+
target_os = "android",
6+
target_os = "freebsd",
7+
target_os = "linux"
8+
))]
59
#[cfg(feature = "uio")]
610
use crate::sys::time::TimeSpec;
711
#[cfg(not(target_os = "redox"))]
@@ -380,6 +384,25 @@ libc_bitflags! {
380384
}
381385
}
382386

387+
#[cfg(target_os = "freebsd")]
388+
libc_enum! {
389+
/// A selector for which clock to use when generating packet timestamps.
390+
/// Used when setting [`TsClock`](crate::sys::socket::sockopt::TsClock) on a socket.
391+
/// (For more details, see [setsockopt(2)](https://man.freebsd.org/cgi/man.cgi?setsockopt)).
392+
#[repr(i32)]
393+
#[non_exhaustive]
394+
pub enum SocketTimestamp {
395+
/// Microsecond resolution, realtime. This is the default.
396+
SO_TS_REALTIME_MICRO,
397+
/// Sub-nanosecond resolution, realtime.
398+
SO_TS_BINTIME,
399+
/// Nanosecond resolution, realtime.
400+
SO_TS_REALTIME,
401+
/// Nanosecond resolution, monotonic.
402+
SO_TS_MONOTONIC,
403+
}
404+
}
405+
383406
cfg_if! {
384407
if #[cfg(any(target_os = "android", target_os = "linux"))] {
385408
/// Unix credentials of the sending process.
@@ -746,6 +769,16 @@ pub enum ControlMessageOwned {
746769
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
747770
#[cfg(any(target_os = "android", target_os = "linux"))]
748771
ScmTimestampns(TimeSpec),
772+
/// Realtime clock timestamp
773+
///
774+
/// [Further reading](https://man.freebsd.org/cgi/man.cgi?setsockopt)
775+
#[cfg(target_os = "freebsd")]
776+
ScmRealtime(TimeSpec),
777+
/// Monotonic clock timestamp
778+
///
779+
/// [Further reading](https://man.freebsd.org/cgi/man.cgi?setsockopt)
780+
#[cfg(target_os = "freebsd")]
781+
ScmMonotonic(TimeSpec),
749782
#[cfg(any(
750783
target_os = "android",
751784
apple_targets,
@@ -926,6 +959,16 @@ impl ControlMessageOwned {
926959
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
927960
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
928961
}
962+
#[cfg(target_os = "freebsd")]
963+
(libc::SOL_SOCKET, libc::SCM_REALTIME) => {
964+
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
965+
ControlMessageOwned::ScmRealtime(TimeSpec::from(ts))
966+
}
967+
#[cfg(target_os = "freebsd")]
968+
(libc::SOL_SOCKET, libc::SCM_MONOTONIC) => {
969+
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
970+
ControlMessageOwned::ScmMonotonic(TimeSpec::from(ts))
971+
}
929972
#[cfg(any(target_os = "android", target_os = "linux"))]
930973
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
931974
let tp = p as *const libc::timespec;

src/sys/socket/sockopt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ sockopt_impl!(
721721
Both,
722722
libc::SOL_SOCKET,
723723
libc::SO_TS_CLOCK,
724-
i32
724+
super::SocketTimestamp
725725
);
726726
#[cfg(any(target_os = "android", target_os = "linux"))]
727727
#[cfg(feature = "net")]

test/sys/test_socket.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,126 @@ pub fn test_timestamping() {
7272
assert!(std::time::Duration::from(diff).as_secs() < 60);
7373
}
7474

75+
#[cfg(target_os = "freebsd")]
76+
#[test]
77+
pub fn test_timestamping_realtime() {
78+
use nix::sys::socket::{
79+
recvmsg, sendmsg, setsockopt, socket, sockopt::ReceiveTimestamp,
80+
sockopt::TsClock, ControlMessageOwned, MsgFlags, SockFlag, SockType,
81+
SockaddrIn, SocketTimestamp,
82+
};
83+
use std::io::{IoSlice, IoSliceMut};
84+
85+
let sock_addr = SockaddrIn::from_str("127.0.0.1:6792").unwrap();
86+
87+
let ssock = socket(
88+
AddressFamily::Inet,
89+
SockType::Datagram,
90+
SockFlag::empty(),
91+
None,
92+
)
93+
.expect("send socket failed");
94+
95+
let rsock = socket(
96+
AddressFamily::Inet,
97+
SockType::Datagram,
98+
SockFlag::empty(),
99+
None,
100+
)
101+
.unwrap();
102+
nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap();
103+
104+
setsockopt(&rsock, ReceiveTimestamp, &true).unwrap();
105+
setsockopt(&rsock, TsClock, &SocketTimestamp::SO_TS_REALTIME).unwrap();
106+
107+
let sbuf = [0u8; 2048];
108+
let mut rbuf = [0u8; 2048];
109+
let flags = MsgFlags::empty();
110+
let iov1 = [IoSlice::new(&sbuf)];
111+
let mut iov2 = [IoSliceMut::new(&mut rbuf)];
112+
113+
let mut cmsg = cmsg_space!(nix::sys::time::TimeVal);
114+
sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
115+
let recv =
116+
recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags)
117+
.unwrap();
118+
119+
let mut ts = None;
120+
for c in recv.cmsgs() {
121+
if let ControlMessageOwned::ScmRealtime(timeval) = c {
122+
ts = Some(timeval);
123+
}
124+
}
125+
let ts = ts.expect("ScmRealtime is present");
126+
let sys_time =
127+
::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME)
128+
.unwrap();
129+
let diff = if ts > sys_time {
130+
ts - sys_time
131+
} else {
132+
sys_time - ts
133+
};
134+
assert!(std::time::Duration::from(diff).as_secs() < 60);
135+
}
136+
137+
#[cfg(target_os = "freebsd")]
138+
#[test]
139+
pub fn test_timestamping_monotonic() {
140+
use nix::sys::socket::{
141+
recvmsg, sendmsg, setsockopt, socket, sockopt::ReceiveTimestamp,
142+
sockopt::TsClock, ControlMessageOwned, MsgFlags, SockFlag, SockType,
143+
SockaddrIn, SocketTimestamp,
144+
};
145+
use std::io::{IoSlice, IoSliceMut};
146+
147+
let sock_addr = SockaddrIn::from_str("127.0.0.1:6803").unwrap();
148+
149+
let ssock = socket(
150+
AddressFamily::Inet,
151+
SockType::Datagram,
152+
SockFlag::empty(),
153+
None,
154+
)
155+
.expect("send socket failed");
156+
157+
let rsock = socket(
158+
AddressFamily::Inet,
159+
SockType::Datagram,
160+
SockFlag::empty(),
161+
None,
162+
)
163+
.unwrap();
164+
nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap();
165+
166+
setsockopt(&rsock, ReceiveTimestamp, &true).unwrap();
167+
setsockopt(&rsock, TsClock, &SocketTimestamp::SO_TS_MONOTONIC).unwrap();
168+
169+
let sbuf = [0u8; 2048];
170+
let mut rbuf = [0u8; 2048];
171+
let flags = MsgFlags::empty();
172+
let iov1 = [IoSlice::new(&sbuf)];
173+
let mut iov2 = [IoSliceMut::new(&mut rbuf)];
174+
175+
let mut cmsg = cmsg_space!(nix::sys::time::TimeVal);
176+
sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
177+
let recv =
178+
recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags)
179+
.unwrap();
180+
181+
let mut ts = None;
182+
for c in recv.cmsgs() {
183+
if let ControlMessageOwned::ScmMonotonic(timeval) = c {
184+
ts = Some(timeval);
185+
}
186+
}
187+
let ts = ts.expect("ScmMonotonic is present");
188+
let sys_time =
189+
::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_MONOTONIC)
190+
.unwrap();
191+
let diff = sys_time - ts; // Monotonic clock sys_time must be greater
192+
assert!(std::time::Duration::from(diff).as_secs() < 60);
193+
}
194+
75195
#[test]
76196
pub fn test_path_to_sock_addr() {
77197
let path = "/foo/bar";

test/sys/test_sockopt.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,3 +436,116 @@ fn test_ipv6_tclass() {
436436
setsockopt(&fd, sockopt::Ipv6TClass, &class).unwrap();
437437
assert_eq!(getsockopt(&fd, sockopt::Ipv6TClass).unwrap(), class);
438438
}
439+
440+
#[test]
441+
#[cfg(target_os = "freebsd")]
442+
fn test_receive_timestamp() {
443+
let fd = socket(
444+
AddressFamily::Inet6,
445+
SockType::Datagram,
446+
SockFlag::empty(),
447+
None,
448+
)
449+
.unwrap();
450+
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();
451+
assert!(getsockopt(&fd, sockopt::ReceiveTimestamp).unwrap());
452+
}
453+
454+
#[test]
455+
#[cfg(target_os = "freebsd")]
456+
fn test_ts_clock_realtime_micro() {
457+
use nix::sys::socket::SocketTimestamp;
458+
459+
let fd = socket(
460+
AddressFamily::Inet6,
461+
SockType::Datagram,
462+
SockFlag::empty(),
463+
None,
464+
)
465+
.unwrap();
466+
467+
// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
468+
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();
469+
470+
setsockopt(
471+
&fd,
472+
sockopt::TsClock,
473+
&SocketTimestamp::SO_TS_REALTIME_MICRO,
474+
)
475+
.unwrap();
476+
assert_eq!(
477+
getsockopt(&fd, sockopt::TsClock).unwrap(),
478+
SocketTimestamp::SO_TS_REALTIME_MICRO
479+
);
480+
}
481+
482+
#[test]
483+
#[cfg(target_os = "freebsd")]
484+
fn test_ts_clock_bintime() {
485+
use nix::sys::socket::SocketTimestamp;
486+
487+
let fd = socket(
488+
AddressFamily::Inet6,
489+
SockType::Datagram,
490+
SockFlag::empty(),
491+
None,
492+
)
493+
.unwrap();
494+
495+
// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
496+
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();
497+
498+
setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_BINTIME).unwrap();
499+
assert_eq!(
500+
getsockopt(&fd, sockopt::TsClock).unwrap(),
501+
SocketTimestamp::SO_TS_BINTIME
502+
);
503+
}
504+
505+
#[test]
506+
#[cfg(target_os = "freebsd")]
507+
fn test_ts_clock_realtime() {
508+
use nix::sys::socket::SocketTimestamp;
509+
510+
let fd = socket(
511+
AddressFamily::Inet6,
512+
SockType::Datagram,
513+
SockFlag::empty(),
514+
None,
515+
)
516+
.unwrap();
517+
518+
// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
519+
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();
520+
521+
setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_REALTIME)
522+
.unwrap();
523+
assert_eq!(
524+
getsockopt(&fd, sockopt::TsClock).unwrap(),
525+
SocketTimestamp::SO_TS_REALTIME
526+
);
527+
}
528+
529+
#[test]
530+
#[cfg(target_os = "freebsd")]
531+
fn test_ts_clock_monotonic() {
532+
use nix::sys::socket::SocketTimestamp;
533+
534+
let fd = socket(
535+
AddressFamily::Inet6,
536+
SockType::Datagram,
537+
SockFlag::empty(),
538+
None,
539+
)
540+
.unwrap();
541+
542+
// FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP.
543+
setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap();
544+
545+
setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_MONOTONIC)
546+
.unwrap();
547+
assert_eq!(
548+
getsockopt(&fd, sockopt::TsClock).unwrap(),
549+
SocketTimestamp::SO_TS_MONOTONIC
550+
);
551+
}

0 commit comments

Comments
 (0)