1547: feat: Add glibc::SOF_TIMESTAMPING_* support r=asomers a=pacak

Support for kernel and hardware receive timestamps

Co-authored-by: Michael Baikov <manpacket@gmail.com>
This commit is contained in:
bors[bot] 2021-12-24 22:20:36 +00:00 committed by GitHub
commit a392647f45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 119 additions and 3 deletions

View File

@ -19,6 +19,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1581](https://github.com/nix-rust/nix/pull/1581))
- Added `sched_setaffinity` and `sched_getaffinity` on DragonFly.
(#[1537](https://github.com/nix-rust/nix/pull/1537))
- Added the `SO_TIMESTAMPING` support
(#[1547](https://github.com/nix-rust/nix/pull/1547))
### Changed
### Fixed

View File

@ -198,6 +198,28 @@ pub enum SockProtocol {
NetlinkCrypto = libc::NETLINK_CRYPTO,
}
#[cfg(any(target_os = "linux"))]
libc_bitflags! {
/// Configuration flags for `SO_TIMESTAMPING` interface
///
/// For use with [`Timestamping`][sockopt::Timestamping].
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
pub struct TimestampingFlag: c_uint {
/// Report any software timestamps when available.
SOF_TIMESTAMPING_SOFTWARE;
/// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
SOF_TIMESTAMPING_RAW_HARDWARE;
/// Collect transmiting timestamps as reported by hardware
SOF_TIMESTAMPING_TX_HARDWARE;
/// Collect transmiting timestamps as reported by software
SOF_TIMESTAMPING_TX_SOFTWARE;
/// Collect receiving timestamps as reported by hardware
SOF_TIMESTAMPING_RX_HARDWARE;
/// Collect receiving timestamps as reported by software
SOF_TIMESTAMPING_RX_SOFTWARE;
}
}
libc_bitflags!{
/// Additional socket options
pub struct SockFlag: c_int {
@ -641,6 +663,11 @@ pub enum ControlMessageOwned {
/// # }
/// ```
ScmTimestamp(TimeVal),
/// A set of nanosecond resolution timestamps
///
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
#[cfg(all(target_os = "linux"))]
ScmTimestampsns(Timestamps),
/// Nanoseconds resolution timestamp
///
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
@ -732,6 +759,18 @@ pub enum ControlMessageOwned {
Unknown(UnknownCmsg),
}
/// For representing packet timestamps via `SO_TIMESTAMPING` interface
#[cfg(all(target_os = "linux"))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Timestamps {
/// software based timestamp, usually one containing data
pub system: TimeSpec,
/// legacy timestamp, usually empty
pub hw_trans: TimeSpec,
/// hardware based timestamp
pub hw_raw: TimeSpec,
}
impl ControlMessageOwned {
/// Decodes a `ControlMessageOwned` from raw bytes.
///
@ -776,6 +815,18 @@ impl ControlMessageOwned {
let ts: libc::timespec = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
}
#[cfg(all(target_os = "linux"))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
let tp = p as *const libc::timespec;
let ts: libc::timespec = ptr::read_unaligned(tp);
let system = TimeSpec::from(ts);
let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
let hw_trans = TimeSpec::from(ts);
let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
let hw_raw = TimeSpec::from(ts);
let timestamping = Timestamps { system, hw_trans, hw_raw };
ControlMessageOwned::ScmTimestampsns(timestamping)
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",

View File

@ -16,7 +16,7 @@ use std::os::unix::ffi::OsStrExt;
// Constants
// TCP_CA_NAME_MAX isn't defined in user space include files
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
const TCP_CA_NAME_MAX: usize = 16;
@ -465,7 +465,12 @@ sockopt_impl!(
#[allow(missing_docs)]
// Not documented by Linux!
Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
sockopt_impl!(
#[cfg(any(target_os = "linux"))]
sockopt_impl!(
/// Specifies exact type of timestamping information collected by the kernel
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
Timestamping, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPING, super::TimestampingFlag);
sockopt_impl!(
/// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
#[cfg(all(target_os = "linux"))]
@ -502,7 +507,7 @@ sockopt_impl!(
/// Enable or disable the receiving of the `SCM_CREDENTIALS` control
/// message.
PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]

View File

@ -57,6 +57,64 @@ pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() {
assert_eq!(from_storage, sockaddr);
}
#[cfg(any(target_os = "linux"))]
#[cfg_attr(qemu, ignore)]
#[test]
pub fn test_timestamping() {
use nix::sys::socket::{
recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping, ControlMessageOwned, MsgFlags,
SockFlag, SockType, TimestampingFlag,
};
use nix::sys::uio::IoVec;
let std_sa = SocketAddr::from_str("127.0.0.1:6790").unwrap();
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);
let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("send socket failed");
let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
nix::sys::socket::bind(rsock, &sock_addr).unwrap();
setsockopt(rsock, Timestamping, &TimestampingFlag::all()).unwrap();
let sbuf = [0u8; 2048];
let mut rbuf = [0u8; 2048];
let flags = MsgFlags::empty();
let iov1 = [IoVec::from_slice(&sbuf)];
let iov2 = [IoVec::from_mut_slice(&mut rbuf)];
let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps);
sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap();
let recv = recvmsg(rsock, &iov2, Some(&mut cmsg), flags).unwrap();
let mut ts = None;
for c in recv.cmsgs() {
if let ControlMessageOwned::ScmTimestampsns(timestamps) = c {
ts = Some(timestamps.system);
}
}
let ts = ts.expect("ScmTimestampns is present");
let sys_time = ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME).unwrap();
let diff = if ts > sys_time {
ts - sys_time
} else {
sys_time - ts
};
assert!(std::time::Duration::from(diff).as_secs() < 60);
}
#[test]
pub fn test_inetv6_addr_to_sock_addr() {
let port: u16 = 3000;