Skip to content

Commit 68d01f0

Browse files
bors[bot]WiSaGaN
andauthored
Merge #1402
1402: Support TIMESTAMPNS r=asomers a=WiSaGaN This adds support of linux TIMESTAMPNS. The code is mostly copied paste from #663 Co-authored-by: Lu, Wangshan <[email protected]>
2 parents 2cef18b + 830bab6 commit 68d01f0

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
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 TIMESTAMPNS support for linux
9+
(#[1402](https://github.com/nix-rust/nix/pull/1402))
810

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ targets = [
3232
]
3333

3434
[dependencies]
35-
libc = { version = "0.2.82", features = [ "extra_traits" ] }
35+
libc = { version = "0.2.93", features = [ "extra_traits" ] }
3636
bitflags = "1.1"
3737
cfg-if = "1.0"
3838

src/sys/socket/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
88
use memoffset::offset_of;
99
use std::{mem, ptr, slice};
1010
use std::os::unix::io::RawFd;
11+
#[cfg(all(target_os = "linux"))]
12+
use crate::sys::time::TimeSpec;
1113
use crate::sys::time::TimeVal;
1214
use crate::sys::uio::IoVec;
1315

@@ -555,6 +557,11 @@ pub enum ControlMessageOwned {
555557
/// # }
556558
/// ```
557559
ScmTimestamp(TimeVal),
560+
/// Nanoseconds resolution timestamp
561+
///
562+
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
563+
#[cfg(all(target_os = "linux"))]
564+
ScmTimestampns(TimeSpec),
558565
#[cfg(any(
559566
target_os = "android",
560567
target_os = "ios",
@@ -646,6 +653,11 @@ impl ControlMessageOwned {
646653
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
647654
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
648655
},
656+
#[cfg(all(target_os = "linux"))]
657+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
658+
let ts: libc::timespec = ptr::read_unaligned(p as *const _);
659+
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
660+
}
649661
#[cfg(any(
650662
target_os = "android",
651663
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"))]
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/sys/test_socket.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,3 +1546,111 @@ pub fn test_vsock() {
15461546
close(s1).unwrap();
15471547
thr.join().unwrap();
15481548
}
1549+
1550+
// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
1551+
// support is suspected.
1552+
#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
1553+
#[cfg(all(target_os = "linux"))]
1554+
#[test]
1555+
fn test_recvmsg_timestampns() {
1556+
use nix::sys::socket::*;
1557+
use nix::sys::uio::IoVec;
1558+
use nix::sys::time::*;
1559+
use std::time::*;
1560+
1561+
// Set up
1562+
let message = "Ohayō!".as_bytes();
1563+
let in_socket = socket(
1564+
AddressFamily::Inet,
1565+
SockType::Datagram,
1566+
SockFlag::empty(),
1567+
None).unwrap();
1568+
setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
1569+
let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
1570+
bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
1571+
let address = getsockname(in_socket).unwrap();
1572+
// Get initial time
1573+
let time0 = SystemTime::now();
1574+
// Send the message
1575+
let iov = [IoVec::from_slice(message)];
1576+
let flags = MsgFlags::empty();
1577+
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
1578+
assert_eq!(message.len(), l);
1579+
// Receive the message
1580+
let mut buffer = vec![0u8; message.len()];
1581+
let mut cmsgspace = nix::cmsg_space!(TimeSpec);
1582+
let iov = [IoVec::from_mut_slice(&mut buffer)];
1583+
let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
1584+
let rtime = match r.cmsgs().next() {
1585+
Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
1586+
Some(_) => panic!("Unexpected control message"),
1587+
None => panic!("No control message")
1588+
};
1589+
// Check the final time
1590+
let time1 = SystemTime::now();
1591+
// the packet's received timestamp should lie in-between the two system
1592+
// times, unless the system clock was adjusted in the meantime.
1593+
let rduration = Duration::new(rtime.tv_sec() as u64,
1594+
rtime.tv_nsec() as u32);
1595+
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
1596+
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
1597+
// Close socket
1598+
nix::unistd::close(in_socket).unwrap();
1599+
}
1600+
1601+
// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
1602+
// support is suspected.
1603+
#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
1604+
#[cfg(all(target_os = "linux"))]
1605+
#[test]
1606+
fn test_recvmmsg_timestampns() {
1607+
use nix::sys::socket::*;
1608+
use nix::sys::uio::IoVec;
1609+
use nix::sys::time::*;
1610+
use std::time::*;
1611+
1612+
// Set up
1613+
let message = "Ohayō!".as_bytes();
1614+
let in_socket = socket(
1615+
AddressFamily::Inet,
1616+
SockType::Datagram,
1617+
SockFlag::empty(),
1618+
None).unwrap();
1619+
setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
1620+
let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
1621+
bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
1622+
let address = getsockname(in_socket).unwrap();
1623+
// Get initial time
1624+
let time0 = SystemTime::now();
1625+
// Send the message
1626+
let iov = [IoVec::from_slice(message)];
1627+
let flags = MsgFlags::empty();
1628+
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
1629+
assert_eq!(message.len(), l);
1630+
// Receive the message
1631+
let mut buffer = vec![0u8; message.len()];
1632+
let mut cmsgspace = nix::cmsg_space!(TimeSpec);
1633+
let iov = [IoVec::from_mut_slice(&mut buffer)];
1634+
let mut data = vec![
1635+
RecvMmsgData {
1636+
iov,
1637+
cmsg_buffer: Some(&mut cmsgspace),
1638+
},
1639+
];
1640+
let r = recvmmsg(in_socket, &mut data, flags, None).unwrap();
1641+
let rtime = match r[0].cmsgs().next() {
1642+
Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
1643+
Some(_) => panic!("Unexpected control message"),
1644+
None => panic!("No control message")
1645+
};
1646+
// Check the final time
1647+
let time1 = SystemTime::now();
1648+
// the packet's received timestamp should lie in-between the two system
1649+
// times, unless the system clock was adjusted in the meantime.
1650+
let rduration = Duration::new(rtime.tv_sec() as u64,
1651+
rtime.tv_nsec() as u32);
1652+
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
1653+
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
1654+
// Close socket
1655+
nix::unistd::close(in_socket).unwrap();
1656+
}

0 commit comments

Comments
 (0)