Skip to content

Commit 5a15829

Browse files
committed
Support linux TIMESTAMPING
1 parent 68d01f0 commit 5a15829

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

src/sys/socket/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,11 @@ pub enum ControlMessageOwned {
562562
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
563563
#[cfg(all(target_os = "linux"))]
564564
ScmTimestampns(TimeSpec),
565+
/// Configurable nanoseconds resolution timestamps
566+
///
567+
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
568+
#[cfg(all(target_os = "linux"))]
569+
ScmTimestamping([TimeSpec; 3]),
565570
#[cfg(any(
566571
target_os = "android",
567572
target_os = "ios",
@@ -658,6 +663,12 @@ impl ControlMessageOwned {
658663
let ts: libc::timespec = ptr::read_unaligned(p as *const _);
659664
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
660665
}
666+
#[cfg(all(target_os = "linux"))]
667+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
668+
let tss: [libc::timespec; 3] = ptr::read_unaligned(p as *const _);
669+
let tss2 = [TimeSpec::from(tss[0]), TimeSpec::from(tss[1]), TimeSpec::from(tss[2])];
670+
ControlMessageOwned::ScmTimestamping(tss2)
671+
}
661672
#[cfg(any(
662673
target_os = "android",
663674
target_os = "freebsd",

src/sys/socket/sockopt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::s
274274
sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
275275
#[cfg(all(target_os = "linux"))]
276276
sockopt_impl!(Both, ReceiveTimestampns, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
277+
#[cfg(all(target_os = "linux"))]
278+
sockopt_impl!(Both, ReceiveTimestamping, libc::SOL_SOCKET, libc::SO_TIMESTAMPING, u32);
277279
#[cfg(any(target_os = "android", target_os = "linux"))]
278280
sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
279281
#[cfg(target_os = "openbsd")]

test/sys/test_socket.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,3 +1654,115 @@ fn test_recvmmsg_timestampns() {
16541654
// Close socket
16551655
nix::unistd::close(in_socket).unwrap();
16561656
}
1657+
1658+
// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
1659+
// support is suspected.
1660+
#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
1661+
#[cfg(all(target_os = "linux"))]
1662+
#[test]
1663+
fn test_recvmsg_timestamping() {
1664+
use nix::sys::socket::*;
1665+
use nix::sys::uio::IoVec;
1666+
use nix::sys::time::*;
1667+
use std::time::*;
1668+
1669+
// Set up
1670+
let message = "Ohayō!".as_bytes();
1671+
let in_socket = socket(
1672+
AddressFamily::Inet,
1673+
SockType::Datagram,
1674+
SockFlag::empty(),
1675+
None).unwrap();
1676+
let flag: u32 = libc::SOF_TIMESTAMPING_RX_SOFTWARE | libc::SOF_TIMESTAMPING_SOFTWARE;
1677+
setsockopt(in_socket, sockopt::ReceiveTimestamping, &flag).unwrap();
1678+
let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
1679+
bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
1680+
let address = getsockname(in_socket).unwrap();
1681+
// Get initial time
1682+
let time0 = SystemTime::now();
1683+
// Send the message
1684+
let iov = [IoVec::from_slice(message)];
1685+
let flags = MsgFlags::empty();
1686+
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
1687+
assert_eq!(message.len(), l);
1688+
// Receive the message
1689+
let mut buffer = vec![0u8; message.len()];
1690+
let mut cmsgspace = nix::cmsg_space!([TimeSpec; 3]);
1691+
let iov = [IoVec::from_mut_slice(&mut buffer)];
1692+
let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
1693+
let times = match r.cmsgs().next() {
1694+
Some(ControlMessageOwned::ScmTimestamping(times)) => times,
1695+
Some(_) => panic!("Unexpected control message"),
1696+
None => panic!("No control message")
1697+
};
1698+
// Check the final time
1699+
let time1 = SystemTime::now();
1700+
// the packet's received timestamp should lie in-between the two system
1701+
// times, unless the system clock was adjusted in the meantime.
1702+
let time = times[0];
1703+
let rduration = Duration::new(time.tv_sec() as u64,
1704+
time.tv_nsec() as u32);
1705+
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
1706+
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
1707+
// Close socket
1708+
nix::unistd::close(in_socket).unwrap();
1709+
}
1710+
1711+
// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
1712+
// support is suspected.
1713+
#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
1714+
#[cfg(all(target_os = "linux"))]
1715+
#[test]
1716+
fn test_recvmmsg_timestamping() {
1717+
use nix::sys::socket::*;
1718+
use nix::sys::uio::IoVec;
1719+
use nix::sys::time::*;
1720+
use std::time::*;
1721+
1722+
// Set up
1723+
let message = "Ohayō!".as_bytes();
1724+
let in_socket = socket(
1725+
AddressFamily::Inet,
1726+
SockType::Datagram,
1727+
SockFlag::empty(),
1728+
None).unwrap();
1729+
let flag: u32 = libc::SOF_TIMESTAMPING_RX_SOFTWARE | libc::SOF_TIMESTAMPING_SOFTWARE;
1730+
setsockopt(in_socket, sockopt::ReceiveTimestamping, &flag).unwrap();
1731+
let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
1732+
bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
1733+
let address = getsockname(in_socket).unwrap();
1734+
// Get initial time
1735+
let time0 = SystemTime::now();
1736+
// Send the message
1737+
let iov = [IoVec::from_slice(message)];
1738+
let flags = MsgFlags::empty();
1739+
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
1740+
assert_eq!(message.len(), l);
1741+
// Receive the message
1742+
let mut buffer = vec![0u8; message.len()];
1743+
let mut cmsgspace = nix::cmsg_space!([TimeSpec; 3]);
1744+
let iov = [IoVec::from_mut_slice(&mut buffer)];
1745+
let mut data = vec![
1746+
RecvMmsgData {
1747+
iov,
1748+
cmsg_buffer: Some(&mut cmsgspace),
1749+
},
1750+
];
1751+
let r = recvmmsg(in_socket, &mut data, flags, None).unwrap();
1752+
let times = match r[0].cmsgs().next() {
1753+
Some(ControlMessageOwned::ScmTimestamping(times)) => times,
1754+
Some(_) => panic!("Unexpected control message"),
1755+
None => panic!("No control message")
1756+
};
1757+
// Check the final time
1758+
let time1 = SystemTime::now();
1759+
// the packet's received timestamp should lie in-between the two system
1760+
// times, unless the system clock was adjusted in the meantime.
1761+
let time = times[0];
1762+
let rduration = Duration::new(time.tv_sec() as u64,
1763+
time.tv_nsec() as u32);
1764+
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
1765+
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
1766+
// Close socket
1767+
nix::unistd::close(in_socket).unwrap();
1768+
}

0 commit comments

Comments
 (0)