Skip to content

Commit 9f4db8a

Browse files
committed
Merge #663
663: Add support for SO_TIMESTAMP r=asomers a=Wolvereness This was implemented as part of my employment, and I have received permission to submit it upstream under my own name. I implemented this with a bit of copy+pasting from the SCM_RIGHTS implementation, and it appeared to be functional when originally implemented on top of 13deb61 (tagged v0.8.0).
2 parents 5c03196 + dba1bb7 commit 9f4db8a

File tree

3 files changed

+106
-3
lines changed

3 files changed

+106
-3
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2424
([#739](https://github.com/nix-rust/nix/pull/739))
2525
- Added nix::sys::ptrace::detach.
2626
([#749](https://github.com/nix-rust/nix/pull/749))
27+
- Added timestamp socket control message variant:
28+
`nix::sys::socket::ControlMessage::ScmTimestamp`
29+
([#663](https://github.com/nix-rust/nix/pull/663))
30+
- Added socket option variant that enables the timestamp socket
31+
control message: `nix::sys::socket::sockopt::ReceiveTimestamp`
32+
([#663](https://github.com/nix-rust/nix/pull/663))
2733

2834
### Changed
2935
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))

src/sys/socket/mod.rs

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use features;
66
use libc::{self, c_void, c_int, socklen_t, size_t, pid_t, uid_t, gid_t};
77
use std::{mem, ptr, slice};
88
use std::os::unix::io::RawFd;
9+
use sys::time::TimeVal;
910
use sys::uio::IoVec;
1011

1112
mod addr;
@@ -277,6 +278,10 @@ impl<'a> Iterator for CmsgIterator<'a> {
277278
slice::from_raw_parts(
278279
&cmsg.cmsg_data as *const _ as *const _, 1)))
279280
},
281+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => unsafe {
282+
Some(ControlMessage::ScmTimestamp(
283+
&*(&cmsg.cmsg_data as *const _ as *const _)))
284+
},
280285
(_, _) => unsafe {
281286
Some(ControlMessage::Unknown(UnknownCmsg(
282287
&cmsg,
@@ -292,11 +297,80 @@ impl<'a> Iterator for CmsgIterator<'a> {
292297
/// be added to this enum; do not exhaustively pattern-match it.
293298
/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html)
294299
pub enum ControlMessage<'a> {
295-
/// A message of type SCM_RIGHTS, containing an array of file
296-
/// descriptors passed between processes. See the description in the
297-
/// "Ancillary messages" section of the
300+
/// A message of type `SCM_RIGHTS`, containing an array of file
301+
/// descriptors passed between processes.
302+
///
303+
/// See the description in the "Ancillary messages" section of the
298304
/// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html).
299305
ScmRights(&'a [RawFd]),
306+
/// A message of type `SCM_TIMESTAMP`, containing the time the
307+
/// packet was received by the kernel.
308+
///
309+
/// See the kernel's explanation in "SO_TIMESTAMP" of
310+
/// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
311+
///
312+
/// # Examples
313+
///
314+
// Disable this test on FreeBSD i386
315+
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222039
316+
#[cfg_attr(not(all(target_os = "freebsd", target_arch = "x86")), doc = " ```")]
317+
#[cfg_attr(all(target_os = "freebsd", target_arch = "x86"), doc = " ```no_run")]
318+
/// use nix::sys::socket::*;
319+
/// use nix::sys::uio::IoVec;
320+
/// use nix::sys::time::*;
321+
/// use std::time::*;
322+
///
323+
/// // Set up
324+
/// let message1 = "Ohayō!".as_bytes();
325+
/// let message2 = "Jā ne".as_bytes();
326+
/// let in_socket = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
327+
/// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
328+
/// bind(in_socket, &SockAddr::new_inet(InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0))).unwrap();
329+
/// let address = if let Ok(address) = getsockname(in_socket) { address } else { unreachable!() };
330+
///
331+
/// // Send both
332+
/// assert!(Ok(message1.len()) == sendmsg(in_socket, &[IoVec::from_slice(message1)], &[], MsgFlags::empty(), Some(&address)));
333+
/// let time = SystemTime::now();
334+
/// std::thread::sleep(Duration::from_millis(250));
335+
/// assert!(Ok(message2.len()) == sendmsg(in_socket, &[IoVec::from_slice(message2)], &[], MsgFlags::empty(), Some(&address)));
336+
/// let delay = time.elapsed().unwrap();
337+
///
338+
/// // Receive the first
339+
/// let mut buffer1 = vec![0u8; message1.len() + message2.len()];
340+
/// let mut time1: CmsgSpace<TimeVal> = CmsgSpace::new();
341+
/// let received1 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer1)], Some(&mut time1), MsgFlags::empty()).unwrap();
342+
/// let mut time1 = if let Some(ControlMessage::ScmTimestamp(&time1)) = received1.cmsgs().next() { time1 } else { panic!("Unexpected or no control message") };
343+
///
344+
/// // Receive the second
345+
/// let mut buffer2 = vec![0u8; message1.len() + message2.len()];
346+
/// let mut time2: CmsgSpace<TimeVal> = CmsgSpace::new();
347+
/// let received2 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer2)], Some(&mut time2), MsgFlags::empty()).unwrap();
348+
/// let mut time2 = if let Some(ControlMessage::ScmTimestamp(&time2)) = received2.cmsgs().next() { time2 } else { panic!("Unexpected or no control message") };
349+
///
350+
/// // Swap if needed; UDP is unordered
351+
/// match (received1.bytes, received2.bytes, message1.len(), message2.len()) {
352+
/// (l1, l2, m1, m2) if l1 == m1 && l2 == m2 => {},
353+
/// (l2, l1, m1, m2) if l1 == m1 && l2 == m2 => {
354+
/// std::mem::swap(&mut time1, &mut time2);
355+
/// std::mem::swap(&mut buffer1, &mut buffer2);
356+
/// },
357+
/// _ => panic!("Wrong packets"),
358+
/// };
359+
///
360+
/// // Compare results
361+
/// println!("{:?} @ {:?}, {:?} @ {:?}, {:?}", buffer1, time1, buffer2, time2, delay);
362+
/// assert!(message1 == &buffer1[0..(message1.len())], "{:?} == {:?}", message1, buffer1);
363+
/// assert!(message2 == &buffer2[0..(message2.len())], "{:?} == {:?}", message2, buffer2);
364+
/// let time = time2 - time1;
365+
/// let time = Duration::new(time.num_seconds() as u64, time.num_nanoseconds() as u32);
366+
/// let difference = if delay < time { time - delay } else { delay - time };
367+
/// assert!(difference.subsec_nanos() < 5_000_000, "{}ns < 5ms", difference.subsec_nanos());
368+
/// assert!(difference.as_secs() == 0);
369+
///
370+
/// // Close socket
371+
/// nix::unistd::close(in_socket).unwrap();
372+
/// ```
373+
ScmTimestamp(&'a TimeVal),
300374
#[doc(hidden)]
301375
Unknown(UnknownCmsg<'a>),
302376
}
@@ -322,6 +396,9 @@ impl<'a> ControlMessage<'a> {
322396
ControlMessage::ScmRights(fds) => {
323397
mem::size_of_val(fds)
324398
},
399+
ControlMessage::ScmTimestamp(t) => {
400+
mem::size_of_val(t)
401+
},
325402
ControlMessage::Unknown(UnknownCmsg(_, bytes)) => {
326403
mem::size_of_val(bytes)
327404
}
@@ -352,6 +429,25 @@ impl<'a> ControlMessage<'a> {
352429

353430
copy_bytes(fds, buf);
354431
},
432+
ControlMessage::ScmTimestamp(t) => {
433+
let cmsg = cmsghdr {
434+
cmsg_len: self.len() as type_of_cmsg_len,
435+
cmsg_level: libc::SOL_SOCKET,
436+
cmsg_type: libc::SCM_TIMESTAMP,
437+
cmsg_data: [],
438+
};
439+
copy_bytes(&cmsg, buf);
440+
441+
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
442+
mem::size_of_val(&cmsg);
443+
444+
let mut tmpbuf = &mut [][..];
445+
mem::swap(&mut tmpbuf, buf);
446+
let (_padding, mut remainder) = tmpbuf.split_at_mut(padlen);
447+
mem::swap(buf, &mut remainder);
448+
449+
copy_bytes(t, buf);
450+
},
355451
ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => {
356452
copy_bytes(orig_cmsg, buf);
357453
copy_bytes(bytes, buf);

src/sys/socket/sockopt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ sockopt_impl!(GetOnly, SockType, libc::SOL_SOCKET, libc::SO_TYPE, super::SockTyp
171171
sockopt_impl!(GetOnly, AcceptConn, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
172172
#[cfg(any(target_os = "linux", target_os = "android"))]
173173
sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
174+
sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
174175

175176
/*
176177
*

0 commit comments

Comments
 (0)