diff --git a/CHANGELOG.md b/CHANGELOG.md index 9636942d5f..0d862def37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate ### Added +- Added linux TIMESTAMPNS support + ([]()) ### Changed - Made `forkpty` unsafe, like `fork` diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 73f976b808..7ff1cc2f1c 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -7,6 +7,8 @@ use libc::{self, c_void, c_int, iovec, socklen_t, size_t, CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN}; use std::{mem, ptr, slice}; use std::os::unix::io::RawFd; +#[cfg(target_os = "linux")] +use crate::sys::time::TimeSpec; use crate::sys::time::TimeVal; use crate::sys::uio::IoVec; @@ -542,6 +544,11 @@ pub enum ControlMessageOwned { /// # } /// ``` ScmTimestamp(TimeVal), + /// Nanoseconds resolution timestamp + /// + /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) + #[cfg(target_os = "linux")] + ScmTimestampNs(TimeSpec), #[cfg(any( target_os = "android", target_os = "ios", @@ -633,6 +640,11 @@ impl ControlMessageOwned { let tv: libc::timeval = ptr::read_unaligned(p as *const _); ControlMessageOwned::ScmTimestamp(TimeVal::from(tv)) }, + #[cfg(target_os = "linux")] + (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => { + let ts: libc::timespec = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmTimestampNs(TimeSpec::from(ts)) + } #[cfg(any( target_os = "android", target_os = "freebsd", diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 5b7b4feafb..f1915f27e8 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -269,6 +269,8 @@ sockopt_impl!(Both, BindToDevice, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsStr #[cfg(any(target_os = "android", target_os = "linux"))] sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in); sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool); +#[cfg(target_os = "linux")] +sockopt_impl!(Both, ReceiveTimestampNs, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool); #[cfg(any(target_os = "android", target_os = "linux"))] sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool); #[cfg(target_os = "openbsd")] diff --git a/test/receive_timestampns.rs b/test/receive_timestampns.rs new file mode 100644 index 0000000000..fb931f5b83 --- /dev/null +++ b/test/receive_timestampns.rs @@ -0,0 +1,45 @@ +use nix::sys::socket::*; +use nix::sys::uio::IoVec; +use nix::sys::time::*; +use std::time::*; + +fn main() { + // Set up + let message = "Ohayƍ!".as_bytes(); + let in_socket = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None).unwrap(); + setsockopt(in_socket, sockopt::ReceiveTimestampNs, &true).unwrap(); + let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + let address = getsockname(in_socket).unwrap(); + // Get initial time + let time0 = SystemTime::now(); + // Send the message + let iov = [IoVec::from_slice(message)]; + let flags = MsgFlags::empty(); + let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + assert_eq!(message.len(), l); + // Receive the message + let mut buffer = vec![0u8; message.len()]; + let mut cmsgspace = nix::cmsg_space!(TimeSpec); + let iov = [IoVec::from_mut_slice(&mut buffer)]; + let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap(); + let rtime = match r.cmsgs().next() { + Some(ControlMessageOwned::ScmTimestampNs(rtime)) => rtime, + Some(_) => panic!("Unexpected control message"), + None => panic!("No control message") + }; + // Check the final time + let time1 = SystemTime::now(); + // the packet's received timestamp should lie in-between the two system + // times, unless the system clock was adjusted in the meantime. + let rduration = Duration::new(rtime.tv_sec() as u64, + rtime.tv_nsec() as u32); + assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); + assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); + // Close socket + nix::unistd::close(in_socket).unwrap(); +}