Skip to content

Commit

Permalink
Support TIMESTAMPNS for linux
Browse files Browse the repository at this point in the history
  • Loading branch information
WiSaGaN committed Apr 8, 2021
1 parent 3f8a66d commit 830bab6
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added
- Added TIMESTAMPNS support for linux
(#[1402](https://github.com/nix-rust/nix/pull/1402))

### Changed
- Made `forkpty` unsafe, like `fork`
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ targets = [
]

[dependencies]
libc = { version = "0.2.82", features = [ "extra_traits" ] }
libc = { version = "0.2.93", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "1.0"

Expand Down
12 changes: 12 additions & 0 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(all(target_os = "linux"))]
use crate::sys::time::TimeSpec;
use crate::sys::time::TimeVal;
use crate::sys::uio::IoVec;

Expand Down Expand Up @@ -554,6 +556,11 @@ pub enum ControlMessageOwned {
/// # }
/// ```
ScmTimestamp(TimeVal),
/// Nanoseconds resolution timestamp
///
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
#[cfg(all(target_os = "linux"))]
ScmTimestampns(TimeSpec),
#[cfg(any(
target_os = "android",
target_os = "ios",
Expand Down Expand Up @@ -645,6 +652,11 @@ impl ControlMessageOwned {
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
},
#[cfg(all(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",
Expand Down
2 changes: 2 additions & 0 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,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(all(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")]
Expand Down
108 changes: 108 additions & 0 deletions test/sys/test_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1535,3 +1535,111 @@ pub fn test_vsock() {
close(s1).unwrap();
thr.join().unwrap();
}

// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
// support is suspected.
#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
#[cfg(all(target_os = "linux"))]
#[test]
fn test_recvmsg_timestampns() {
use nix::sys::socket::*;
use nix::sys::uio::IoVec;
use nix::sys::time::*;
use std::time::*;

// 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();
}

// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
// support is suspected.
#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
#[cfg(all(target_os = "linux"))]
#[test]
fn test_recvmmsg_timestampns() {
use nix::sys::socket::*;
use nix::sys::uio::IoVec;
use nix::sys::time::*;
use std::time::*;

// 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 mut data = vec![
RecvMmsgData {
iov,
cmsg_buffer: Some(&mut cmsgspace),
},
];
let r = recvmmsg(in_socket, &mut data, flags, None).unwrap();
let rtime = match r[0].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();
}

0 comments on commit 830bab6

Please sign in to comment.