Skip to content

Commit 13bc324

Browse files
committed
Support TIMESTAMPNS
1 parent 5e491c8 commit 13bc324

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55

66
## [Unreleased] - ReleaseDate
77
### Added
8+
- Added linux TIMESTAMPNS support for x86/x64 gnu
9+
(#[1402](https://github.com/nix-rust/nix/pull/1402))
810

911
### Changed
1012
- Made `forkpty` unsafe, like `fork`

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,7 @@ harness = false
7676
[[test]]
7777
name = "test-ptymaster-drop"
7878
path = "test/test_ptymaster_drop.rs"
79+
80+
[[test]]
81+
name = "test-receive-timestampns"
82+
path = "test/test_receive_timestampns.rs"

src/sys/socket/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
77
CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN};
88
use std::{mem, ptr, slice};
99
use std::os::unix::io::RawFd;
10+
#[cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86"), target_env = "gnu"))]
11+
use crate::sys::time::TimeSpec;
1012
use crate::sys::time::TimeVal;
1113
use crate::sys::uio::IoVec;
1214

@@ -556,6 +558,11 @@ pub enum ControlMessageOwned {
556558
/// # }
557559
/// ```
558560
ScmTimestamp(TimeVal),
561+
/// Nanoseconds resolution timestamp
562+
///
563+
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
564+
#[cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86"), target_env = "gnu"))]
565+
ScmTimestampNs(TimeSpec),
559566
#[cfg(any(
560567
target_os = "android",
561568
target_os = "ios",
@@ -647,6 +654,11 @@ impl ControlMessageOwned {
647654
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
648655
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
649656
},
657+
#[cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86"), target_env = "gnu"))]
658+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
659+
let ts: libc::timespec = ptr::read_unaligned(p as *const _);
660+
ControlMessageOwned::ScmTimestampNs(TimeSpec::from(ts))
661+
}
650662
#[cfg(any(
651663
target_os = "android",
652664
target_os = "freebsd",

src/sys/socket/sockopt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ sockopt_impl!(Both, BindToDevice, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsStr
272272
#[cfg(any(target_os = "android", target_os = "linux"))]
273273
sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
274274
sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
275+
#[cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86"), target_env = "gnu"))]
276+
sockopt_impl!(Both, ReceiveTimestampNs, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
275277
#[cfg(any(target_os = "android", target_os = "linux"))]
276278
sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
277279
#[cfg(target_os = "openbsd")]

test/test_receive_timestampns.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use nix::sys::socket::*;
2+
use nix::sys::uio::IoVec;
3+
use nix::sys::time::*;
4+
use std::time::*;
5+
6+
// TODO: move this to unit test once we may use `cmsg_space!` there
7+
#[cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86"), target_env = "gnu"))]
8+
#[test]
9+
fn recvmsg_timestampns() {
10+
// Set up
11+
let message = "Ohayō!".as_bytes();
12+
let in_socket = socket(
13+
AddressFamily::Inet,
14+
SockType::Datagram,
15+
SockFlag::empty(),
16+
None).unwrap();
17+
setsockopt(in_socket, sockopt::ReceiveTimestampNs, &true).unwrap();
18+
let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
19+
bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
20+
let address = getsockname(in_socket).unwrap();
21+
// Get initial time
22+
let time0 = SystemTime::now();
23+
// Send the message
24+
let iov = [IoVec::from_slice(message)];
25+
let flags = MsgFlags::empty();
26+
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
27+
assert_eq!(message.len(), l);
28+
// Receive the message
29+
let mut buffer = vec![0u8; message.len()];
30+
let mut cmsgspace = nix::cmsg_space!(TimeSpec);
31+
let iov = [IoVec::from_mut_slice(&mut buffer)];
32+
let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
33+
let rtime = match r.cmsgs().next() {
34+
Some(ControlMessageOwned::ScmTimestampNs(rtime)) => rtime,
35+
Some(_) => panic!("Unexpected control message"),
36+
None => panic!("No control message")
37+
};
38+
// Check the final time
39+
let time1 = SystemTime::now();
40+
// the packet's received timestamp should lie in-between the two system
41+
// times, unless the system clock was adjusted in the meantime.
42+
let rduration = Duration::new(rtime.tv_sec() as u64,
43+
rtime.tv_nsec() as u32);
44+
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
45+
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
46+
// Close socket
47+
nix::unistd::close(in_socket).unwrap();
48+
}
49+
50+
#[cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86"), target_env = "gnu"))]
51+
#[test]
52+
fn recvmmsg_timestampns() {
53+
// Set up
54+
let message = "Ohayō!".as_bytes();
55+
let in_socket = socket(
56+
AddressFamily::Inet,
57+
SockType::Datagram,
58+
SockFlag::empty(),
59+
None).unwrap();
60+
setsockopt(in_socket, sockopt::ReceiveTimestampNs, &true).unwrap();
61+
let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
62+
bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
63+
let address = getsockname(in_socket).unwrap();
64+
// Get initial time
65+
let time0 = SystemTime::now();
66+
// Send the message
67+
let iov = [IoVec::from_slice(message)];
68+
let flags = MsgFlags::empty();
69+
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
70+
assert_eq!(message.len(), l);
71+
// Receive the message
72+
let mut buffer = vec![0u8; message.len()];
73+
let mut cmsgspace = nix::cmsg_space!(TimeSpec);
74+
let iov = [IoVec::from_mut_slice(&mut buffer)];
75+
let mut data = vec![
76+
RecvMmsgData {
77+
iov,
78+
cmsg_buffer: Some(&mut cmsgspace),
79+
},
80+
];
81+
let r = recvmmsg(in_socket, &mut data, flags, None).unwrap();
82+
let rtime = match r[0].cmsgs().next() {
83+
Some(ControlMessageOwned::ScmTimestampNs(rtime)) => rtime,
84+
Some(_) => panic!("Unexpected control message"),
85+
None => panic!("No control message")
86+
};
87+
// Check the final time
88+
let time1 = SystemTime::now();
89+
// the packet's received timestamp should lie in-between the two system
90+
// times, unless the system clock was adjusted in the meantime.
91+
let rduration = Duration::new(rtime.tv_sec() as u64,
92+
rtime.tv_nsec() as u32);
93+
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
94+
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
95+
// Close socket
96+
nix::unistd::close(in_socket).unwrap();
97+
}

0 commit comments

Comments
 (0)