Skip to content

Commit

Permalink
feat: Add glibc::SOF_TIMESTAMPING_* support
Browse files Browse the repository at this point in the history
  • Loading branch information
pacak committed Oct 10, 2021
1 parent da49e4f commit c1f2426
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 4 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Expand Up @@ -3,9 +3,14 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [0.23.0] - 2021-09-28

## [Unreleased] - ReleaseDate
### Added
- Added the `SO_TIMESTAMPING` support
(#[1547](https://github.com/nix-rust/nix/pull/1547))

## [0.23.0] - 2021-09-28
### Added
- Added the `LocalPeerCred` sockopt.
(#[1482](https://github.com/nix-rust/nix/pull/1482))
- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec`
Expand Down
51 changes: 51 additions & 0 deletions src/sys/socket/mod.rs
Expand Up @@ -170,6 +170,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 {
Expand Down Expand Up @@ -591,6 +613,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)
Expand Down Expand Up @@ -666,6 +693,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.
///
Expand Down Expand Up @@ -710,6 +749,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",
Expand Down
11 changes: 8 additions & 3 deletions src/sys/socket/sockopt.rs
Expand Up @@ -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"))]
const TCP_CA_NAME_MAX: usize = 16;

/// Helper for implementing `SetSockOpt` for a given socket option. See
Expand Down Expand Up @@ -432,7 +432,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"))]
Expand Down Expand Up @@ -463,7 +468,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"))]
sockopt_impl!(
/// This option allows the caller to set the TCP congestion control
/// algorithm to be used, on a per-socket basis.
Expand Down
58 changes: 58 additions & 0 deletions test/sys/test_socket.rs
Expand Up @@ -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;
Expand Down

0 comments on commit c1f2426

Please sign in to comment.