Skip to content

Commit 54d4a37

Browse files
committed
Add support for SO_TIMESTAMP
1 parent 087aece commit 54d4a37

File tree

3 files changed

+106
-2
lines changed

3 files changed

+106
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1414
([#647](https://github.com/nix-rust/nix/pull/647))
1515
- Added the `pid()` method to `WaitStatus` for extracting the PID.
1616
([#722](https://github.com/nix-rust/nix/pull/722))
17+
- Added timestamp socket control message variant:
18+
`nix::sys::socket::ControlMessage::ScmTimestamp`
19+
([#663](https://github.com/nix-rust/nix/pull/663))
20+
- Added socket option variant that enables the timestamp socket
21+
control message: `nix::sys::socket::sockopt::ReceiveTimestamp`
22+
([#663](https://github.com/nix-rust/nix/pull/663))
1723

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

src/sys/socket/mod.rs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ impl<'a> Iterator for CmsgIterator<'a> {
258258
slice::from_raw_parts(
259259
&cmsg.cmsg_data as *const _ as *const _, 1)))
260260
},
261+
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
262+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => unsafe {
263+
Some(ControlMessage::ScmTimestamp(
264+
&*(&cmsg.cmsg_data as *const _ as *const _)))
265+
},
261266
(_, _) => unsafe {
262267
Some(ControlMessage::Unknown(UnknownCmsg(
263268
&cmsg,
@@ -274,10 +279,77 @@ impl<'a> Iterator for CmsgIterator<'a> {
274279
/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html)
275280
pub enum ControlMessage<'a> {
276281
/// A message of type SCM_RIGHTS, containing an array of file
277-
/// descriptors passed between processes. See the description in the
278-
/// "Ancillary messages" section of the
282+
/// descriptors passed between processes.
283+
///
284+
/// See the description in the "Ancillary messages" section of the
279285
/// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html).
280286
ScmRights(&'a [RawFd]),
287+
/// A message of type SCM_TIMESTAMP, containing the time the packet
288+
/// was received by the kernel.
289+
///
290+
/// See the kernel's explanation in "SO_TIMESTAMP" of
291+
/// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
292+
///
293+
/// # Examples
294+
///
295+
/// ```
296+
/// use nix::sys::socket::*;
297+
/// use nix::sys::uio::IoVec;
298+
/// use nix::sys::time::*;
299+
/// use std::time::*;
300+
///
301+
/// // Set up
302+
/// let message1 = "Ohayō!".as_bytes();
303+
/// let message2 = "Jā ne".as_bytes();
304+
/// let in_socket = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
305+
/// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
306+
/// bind(in_socket, &SockAddr::new_inet(InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0))).unwrap();
307+
/// let address = if let Ok(address) = getsockname(in_socket) { address } else { unreachable!() };
308+
///
309+
/// // Send both
310+
/// assert!(Ok(message1.len()) == sendmsg(in_socket, &[IoVec::from_slice(message1)], &[], MsgFlags::empty(), Some(&address)));
311+
/// let time = SystemTime::now();
312+
/// std::thread::sleep(Duration::from_millis(250));
313+
/// assert!(Ok(message2.len()) == sendmsg(in_socket, &[IoVec::from_slice(message2)], &[], MsgFlags::empty(), Some(&address)));
314+
/// let delay = time.elapsed().unwrap();
315+
///
316+
/// // Receive the first
317+
/// let mut buffer1 = vec![0u8; message1.len() + message2.len()];
318+
/// let mut time1: CmsgSpace<TimeVal> = CmsgSpace::new();
319+
/// let received1 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer1)], Some(&mut time1), MsgFlags::empty()).unwrap();
320+
/// let mut time1 = if let Some(ControlMessage::ScmTimestamp(&time1)) = received1.cmsgs().next() { time1 } else { panic!("Unexpected or no control message") };
321+
///
322+
/// // Receive the second
323+
/// let mut buffer2 = vec![0u8; message1.len() + message2.len()];
324+
/// let mut time2: CmsgSpace<TimeVal> = CmsgSpace::new();
325+
/// let received2 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer2)], Some(&mut time2), MsgFlags::empty()).unwrap();
326+
/// let mut time2 = if let Some(ControlMessage::ScmTimestamp(&time2)) = received2.cmsgs().next() { time2 } else { panic!("Unexpected or no control message") };
327+
///
328+
/// // Swap if needed; UDP is unordered
329+
/// match (received1.bytes, received2.bytes, message1.len(), message2.len()) {
330+
/// (l1, l2, m1, m2) if l1 == m1 && l2 == m2 => {},
331+
/// (l2, l1, m1, m2) if l1 == m1 && l2 == m2 => {
332+
/// std::mem::swap(&mut time1, &mut time2);
333+
/// std::mem::swap(&mut buffer1, &mut buffer2);
334+
/// },
335+
/// _ => panic!("Wrong packets"),
336+
/// };
337+
///
338+
/// // Compare results
339+
/// assert!(message1 == &buffer1[0..(message1.len())], "{:?} == {:?}", message1, buffer1);
340+
/// assert!(message2 == &buffer2[0..(message2.len())], "{:?} == {:?}", message2, buffer2);
341+
/// let time = time2 - time1;
342+
/// let time = Duration::new(time.num_seconds() as u64, time.num_nanoseconds() as u32);
343+
/// let difference = if delay < time { time - delay } else { delay - time };
344+
/// assert!(difference.subsec_nanos() < 15_000, "{}ns < 15ms", difference.subsec_nanos());
345+
/// assert!(difference.as_secs() == 0);
346+
/// println!("{:?} @ {:?}, {:?} @ {:?}, {:?}", buffer1, time1, buffer2, time2, delay);
347+
///
348+
/// // Close socket
349+
/// nix::unistd::close(in_socket).unwrap();
350+
/// ```
351+
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
352+
ScmTimestamp(&'a ::sys::time::TimeVal),
281353
#[doc(hidden)]
282354
Unknown(UnknownCmsg<'a>),
283355
}
@@ -303,6 +375,10 @@ impl<'a> ControlMessage<'a> {
303375
ControlMessage::ScmRights(fds) => {
304376
mem::size_of_val(fds)
305377
},
378+
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
379+
ControlMessage::ScmTimestamp(t) => {
380+
mem::size_of_val(t)
381+
},
306382
ControlMessage::Unknown(UnknownCmsg(_, bytes)) => {
307383
mem::size_of_val(bytes)
308384
}
@@ -333,6 +409,26 @@ impl<'a> ControlMessage<'a> {
333409

334410
copy_bytes(fds, buf);
335411
},
412+
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
413+
ControlMessage::ScmTimestamp(t) => {
414+
let cmsg = cmsghdr {
415+
cmsg_len: self.len() as type_of_cmsg_len,
416+
cmsg_level: libc::SOL_SOCKET,
417+
cmsg_type: libc::SCM_TIMESTAMP,
418+
cmsg_data: [],
419+
};
420+
copy_bytes(&cmsg, buf);
421+
422+
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
423+
mem::size_of_val(&cmsg);
424+
425+
let mut tmpbuf = &mut [][..];
426+
mem::swap(&mut tmpbuf, buf);
427+
let (_padding, mut remainder) = tmpbuf.split_at_mut(padlen);
428+
mem::swap(buf, &mut remainder);
429+
430+
copy_bytes(t, buf);
431+
},
336432
ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => {
337433
copy_bytes(orig_cmsg, buf);
338434
copy_bytes(bytes, buf);

src/sys/socket/sockopt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ 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+
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
175+
sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
174176

175177
/*
176178
*

0 commit comments

Comments
 (0)