summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/sys/socket/mod.rs51
-rw-r--r--src/sys/socket/sockopt.rs11
-rw-r--r--test/sys/test_socket.rs58
4 files changed, 119 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4311668..cd0d9e12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index dcac2818..9df5ed4a 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -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",
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index 3ad139ad..056ded42 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -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")))]
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 0f6fac66..7a861abf 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -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;