Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add glibc::SOF_TIMESTAMPING_* support #1547

Merged
merged 1 commit into from Dec 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions src/sys/socket/mod.rs
Expand Up @@ -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 {
pacak marked this conversation as resolved.
Show resolved Hide resolved
/// 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 @@ -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)
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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",
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"))]
#[cfg(feature = "net")]
const TCP_CA_NAME_MAX: usize = 16;

Expand Down Expand Up @@ -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"))]
Expand Down Expand Up @@ -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")))]
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