From a9829853df9192048be39cb9593206b8492748f6 Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Wed, 25 Aug 2021 14:31:57 +0100 Subject: [PATCH] Add support for the SO_TXTIME sockopt and SCM_TXTIME control message --- CHANGELOG.md | 2 ++ src/sys/socket/mod.rs | 22 +++++++++++++++ src/sys/socket/sockopt.rs | 5 ++++ test/sys/test_socket.rs | 57 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23c5c0887c..4c4516ae0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). (#[1547](https://github.com/nix-rust/nix/pull/1547)) - Added getter methods to `MqAttr` struct (#[1619](https://github.com/nix-rust/nix/pull/1619)) +- Added the `TxTime` sockopt and control message. + (#[1564](https://github.com/nix-rust/nix/pull/1564)) ### Changed ### Fixed diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 9df5ed4a9b..f6d37c99e9 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -1056,6 +1056,14 @@ pub enum ControlMessage<'a> { #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] RxqOvfl(&'a u32), + + /// Configure the transmission time of packets. + /// + /// For further information, please refer to the + /// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man + /// page. + #[cfg(target_os = "linux")] + TxTime(&'a u64), } // An opaque structure used to prevent cmsghdr from being a public type @@ -1153,6 +1161,10 @@ impl<'a> ControlMessage<'a> { ControlMessage::RxqOvfl(drop_count) => { drop_count as *const _ as *const u8 }, + #[cfg(target_os = "linux")] + ControlMessage::TxTime(tx_time) => { + tx_time as *const _ as *const u8 + }, }; unsafe { ptr::copy_nonoverlapping( @@ -1208,6 +1220,10 @@ impl<'a> ControlMessage<'a> { ControlMessage::RxqOvfl(drop_count) => { mem::size_of_val(drop_count) }, + #[cfg(target_os = "linux")] + ControlMessage::TxTime(tx_time) => { + mem::size_of_val(tx_time) + }, } } @@ -1237,6 +1253,8 @@ impl<'a> ControlMessage<'a> { ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6, #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET, + #[cfg(target_os = "linux")] + ControlMessage::TxTime(_) => libc::SOL_SOCKET, } } @@ -1279,6 +1297,10 @@ impl<'a> ControlMessage<'a> { ControlMessage::RxqOvfl(_) => { libc::SO_RXQ_OVFL }, + #[cfg(target_os = "linux")] + ControlMessage::TxTime(_) => { + libc::SCM_TXTIME + }, } } diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 056ded429e..c600391c64 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -582,6 +582,11 @@ sockopt_impl!( #[allow(missing_docs)] // Not documented by Linux! UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool); +#[cfg(target_os = "linux")] +sockopt_impl!( + /// Configures the behavior of time-based transmission of packets, for use + /// with the `TxTime` control message. + TxTime, Both, libc::SOL_SOCKET, libc::SO_TXTIME, libc::sock_txtime); #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] sockopt_impl!( /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 7a861abfdd..5262d3b813 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -1997,3 +1997,60 @@ mod linux_errqueue { assert_eq!(ext_err.ee_info, 0); } } + +// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack +// of QEMU support is suspected. +#[cfg_attr(qemu, ignore)] +#[cfg(target_os = "linux")] +#[test] +pub fn test_txtime() { + use nix::sys::socket::{ + bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage, + MsgFlags, SockFlag, SockType, + }; + use nix::sys::time::TimeValLike; + use nix::time::{ClockId, clock_gettime}; + + require_kernel_version!(test_txtime, ">= 5.8"); + + 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 txtime_cfg = libc::sock_txtime { + clockid: libc::CLOCK_MONOTONIC, + flags: 0, + }; + setsockopt(ssock, sockopt::TxTime, &txtime_cfg).unwrap(); + + let rsock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + bind(rsock, &sock_addr).unwrap(); + + let sbuf = [0u8; 2048]; + let iov1 = [nix::sys::uio::IoVec::from_slice(&sbuf)]; + + let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + let delay = std::time::Duration::from_secs(1).into(); + let txtime = (now + delay).num_nanoseconds() as u64; + + let cmsg = ControlMessage::TxTime(&txtime); + sendmsg(ssock, &iov1, &[cmsg], MsgFlags::empty(), Some(&sock_addr)).unwrap(); + + let mut rbuf = [0u8; 2048]; + let iov2 = [nix::sys::uio::IoVec::from_mut_slice(&mut rbuf)]; + recvmsg(rsock, &iov2, None, MsgFlags::empty()).unwrap(); +}