Skip to content

Commit a392647

Browse files
bors[bot]pacak
andauthored
1547: feat: Add glibc::SOF_TIMESTAMPING_* support r=asomers a=pacak Support for kernel and hardware receive timestamps Co-authored-by: Michael Baikov <[email protected]>
2 parents d38a656 + 9aa61ef commit a392647

File tree

4 files changed

+119
-3
lines changed

4 files changed

+119
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1919
(#[1581](https://github.com/nix-rust/nix/pull/1581))
2020
- Added `sched_setaffinity` and `sched_getaffinity` on DragonFly.
2121
(#[1537](https://github.com/nix-rust/nix/pull/1537))
22+
- Added the `SO_TIMESTAMPING` support
23+
(#[1547](https://github.com/nix-rust/nix/pull/1547))
2224

2325
### Changed
2426
### Fixed

src/sys/socket/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,28 @@ pub enum SockProtocol {
198198
NetlinkCrypto = libc::NETLINK_CRYPTO,
199199
}
200200

201+
#[cfg(any(target_os = "linux"))]
202+
libc_bitflags! {
203+
/// Configuration flags for `SO_TIMESTAMPING` interface
204+
///
205+
/// For use with [`Timestamping`][sockopt::Timestamping].
206+
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
207+
pub struct TimestampingFlag: c_uint {
208+
/// Report any software timestamps when available.
209+
SOF_TIMESTAMPING_SOFTWARE;
210+
/// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
211+
SOF_TIMESTAMPING_RAW_HARDWARE;
212+
/// Collect transmiting timestamps as reported by hardware
213+
SOF_TIMESTAMPING_TX_HARDWARE;
214+
/// Collect transmiting timestamps as reported by software
215+
SOF_TIMESTAMPING_TX_SOFTWARE;
216+
/// Collect receiving timestamps as reported by hardware
217+
SOF_TIMESTAMPING_RX_HARDWARE;
218+
/// Collect receiving timestamps as reported by software
219+
SOF_TIMESTAMPING_RX_SOFTWARE;
220+
}
221+
}
222+
201223
libc_bitflags!{
202224
/// Additional socket options
203225
pub struct SockFlag: c_int {
@@ -641,6 +663,11 @@ pub enum ControlMessageOwned {
641663
/// # }
642664
/// ```
643665
ScmTimestamp(TimeVal),
666+
/// A set of nanosecond resolution timestamps
667+
///
668+
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
669+
#[cfg(all(target_os = "linux"))]
670+
ScmTimestampsns(Timestamps),
644671
/// Nanoseconds resolution timestamp
645672
///
646673
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
@@ -732,6 +759,18 @@ pub enum ControlMessageOwned {
732759
Unknown(UnknownCmsg),
733760
}
734761

762+
/// For representing packet timestamps via `SO_TIMESTAMPING` interface
763+
#[cfg(all(target_os = "linux"))]
764+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
765+
pub struct Timestamps {
766+
/// software based timestamp, usually one containing data
767+
pub system: TimeSpec,
768+
/// legacy timestamp, usually empty
769+
pub hw_trans: TimeSpec,
770+
/// hardware based timestamp
771+
pub hw_raw: TimeSpec,
772+
}
773+
735774
impl ControlMessageOwned {
736775
/// Decodes a `ControlMessageOwned` from raw bytes.
737776
///
@@ -776,6 +815,18 @@ impl ControlMessageOwned {
776815
let ts: libc::timespec = ptr::read_unaligned(p as *const _);
777816
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
778817
}
818+
#[cfg(all(target_os = "linux"))]
819+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
820+
let tp = p as *const libc::timespec;
821+
let ts: libc::timespec = ptr::read_unaligned(tp);
822+
let system = TimeSpec::from(ts);
823+
let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
824+
let hw_trans = TimeSpec::from(ts);
825+
let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
826+
let hw_raw = TimeSpec::from(ts);
827+
let timestamping = Timestamps { system, hw_trans, hw_raw };
828+
ControlMessageOwned::ScmTimestampsns(timestamping)
829+
}
779830
#[cfg(any(
780831
target_os = "android",
781832
target_os = "freebsd",

src/sys/socket/sockopt.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::os::unix::ffi::OsStrExt;
1616

1717
// Constants
1818
// TCP_CA_NAME_MAX isn't defined in user space include files
19-
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
19+
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
2020
#[cfg(feature = "net")]
2121
const TCP_CA_NAME_MAX: usize = 16;
2222

@@ -465,7 +465,12 @@ sockopt_impl!(
465465
#[allow(missing_docs)]
466466
// Not documented by Linux!
467467
Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
468-
sockopt_impl!(
468+
#[cfg(any(target_os = "linux"))]
469+
sockopt_impl!(
470+
/// Specifies exact type of timestamping information collected by the kernel
471+
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
472+
Timestamping, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPING, super::TimestampingFlag);
473+
sockopt_impl!(
469474
/// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
470475
ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
471476
#[cfg(all(target_os = "linux"))]
@@ -502,7 +507,7 @@ sockopt_impl!(
502507
/// Enable or disable the receiving of the `SCM_CREDENTIALS` control
503508
/// message.
504509
PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
505-
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
510+
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
506511
#[cfg(feature = "net")]
507512
sockopt_impl!(
508513
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]

test/sys/test_socket.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,64 @@ pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() {
5757
assert_eq!(from_storage, sockaddr);
5858
}
5959

60+
#[cfg(any(target_os = "linux"))]
61+
#[cfg_attr(qemu, ignore)]
62+
#[test]
63+
pub fn test_timestamping() {
64+
use nix::sys::socket::{
65+
recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping, ControlMessageOwned, MsgFlags,
66+
SockFlag, SockType, TimestampingFlag,
67+
};
68+
use nix::sys::uio::IoVec;
69+
70+
let std_sa = SocketAddr::from_str("127.0.0.1:6790").unwrap();
71+
let inet_addr = InetAddr::from_std(&std_sa);
72+
let sock_addr = SockAddr::new_inet(inet_addr);
73+
74+
let ssock = socket(
75+
AddressFamily::Inet,
76+
SockType::Datagram,
77+
SockFlag::empty(),
78+
None,
79+
)
80+
.expect("send socket failed");
81+
82+
let rsock = socket(
83+
AddressFamily::Inet,
84+
SockType::Datagram,
85+
SockFlag::empty(),
86+
None,
87+
)
88+
.unwrap();
89+
nix::sys::socket::bind(rsock, &sock_addr).unwrap();
90+
91+
setsockopt(rsock, Timestamping, &TimestampingFlag::all()).unwrap();
92+
93+
let sbuf = [0u8; 2048];
94+
let mut rbuf = [0u8; 2048];
95+
let flags = MsgFlags::empty();
96+
let iov1 = [IoVec::from_slice(&sbuf)];
97+
let iov2 = [IoVec::from_mut_slice(&mut rbuf)];
98+
let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps);
99+
sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap();
100+
let recv = recvmsg(rsock, &iov2, Some(&mut cmsg), flags).unwrap();
101+
102+
let mut ts = None;
103+
for c in recv.cmsgs() {
104+
if let ControlMessageOwned::ScmTimestampsns(timestamps) = c {
105+
ts = Some(timestamps.system);
106+
}
107+
}
108+
let ts = ts.expect("ScmTimestampns is present");
109+
let sys_time = ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME).unwrap();
110+
let diff = if ts > sys_time {
111+
ts - sys_time
112+
} else {
113+
sys_time - ts
114+
};
115+
assert!(std::time::Duration::from(diff).as_secs() < 60);
116+
}
117+
60118
#[test]
61119
pub fn test_inetv6_addr_to_sock_addr() {
62120
let port: u16 = 3000;

0 commit comments

Comments
 (0)