Skip to content

Commit

Permalink
Add support for SO_TIMESTAMP
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolvereness committed Aug 9, 2017
1 parent 087aece commit 8a7c977
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#647](https://github.com/nix-rust/nix/pull/647))
- Added the `pid()` method to `WaitStatus` for extracting the PID.
([#722](https://github.com/nix-rust/nix/pull/722))
- Added timestamp socket control message variant:
`nix::sys::socket::ControlMessage::ScmTimestamp`
([#663](https://github.com/nix-rust/nix/pull/663))
- Added socket option variant that enables the timestamp socket
control message: `nix::sys::socket::sockopt::ReceiveTimestamp`
([#663](https://github.com/nix-rust/nix/pull/663))

### Changed
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
Expand Down
100 changes: 98 additions & 2 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ impl<'a> Iterator for CmsgIterator<'a> {
slice::from_raw_parts(
&cmsg.cmsg_data as *const _ as *const _, 1)))
},
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => unsafe {
Some(ControlMessage::ScmTimestamp(
&*(&cmsg.cmsg_data as *const _ as *const _)))
},
(_, _) => unsafe {
Some(ControlMessage::Unknown(UnknownCmsg(
&cmsg,
Expand All @@ -274,10 +279,77 @@ impl<'a> Iterator for CmsgIterator<'a> {
/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html)
pub enum ControlMessage<'a> {
/// A message of type SCM_RIGHTS, containing an array of file
/// descriptors passed between processes. See the description in the
/// "Ancillary messages" section of the
/// descriptors passed between processes.
///
/// See the description in the "Ancillary messages" section of the
/// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html).
ScmRights(&'a [RawFd]),
/// A message of type SCM_TIMESTAMP, containing the time the packet
/// was received by the kernel.
///
/// See the kernel's explanation in "SO_TIMESTAMP" of
/// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
///
/// # Examples
///
/// ```
/// use nix::sys::socket::*;
/// use nix::sys::uio::IoVec;
/// use nix::sys::time::*;
/// use std::time::*;
///
/// // Set up
/// let message1 = "Ohayō!".as_bytes();
/// let message2 = "Jā ne".as_bytes();
/// let in_socket = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
/// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
/// bind(in_socket, &SockAddr::new_inet(InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0))).unwrap();
/// let address = if let Ok(address) = getsockname(in_socket) { address } else { unreachable!() };
///
/// // Send both
/// assert!(Ok(message1.len()) == sendmsg(in_socket, &[IoVec::from_slice(message1)], &[], MsgFlags::empty(), Some(&address)));
/// let time = SystemTime::now();
/// std::thread::sleep(Duration::from_millis(250));
/// assert!(Ok(message2.len()) == sendmsg(in_socket, &[IoVec::from_slice(message2)], &[], MsgFlags::empty(), Some(&address)));
/// let delay = time.elapsed().unwrap();
///
/// // Receive the first
/// let mut buffer1 = vec![0u8; message1.len() + message2.len()];
/// let mut time1: CmsgSpace<TimeVal> = CmsgSpace::new();
/// let received1 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer1)], Some(&mut time1), MsgFlags::empty()).unwrap();
/// let mut time1 = if let Some(ControlMessage::ScmTimestamp(&time1)) = received1.cmsgs().next() { time1 } else { panic!("Unexpected or no control message") };
///
/// // Receive the second
/// let mut buffer2 = vec![0u8; message1.len() + message2.len()];
/// let mut time2: CmsgSpace<TimeVal> = CmsgSpace::new();
/// let received2 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer2)], Some(&mut time2), MsgFlags::empty()).unwrap();
/// let mut time2 = if let Some(ControlMessage::ScmTimestamp(&time2)) = received2.cmsgs().next() { time2 } else { panic!("Unexpected or no control message") };
///
/// // Swap if needed; UDP is unordered
/// match (received1.bytes, received2.bytes, message1.len(), message2.len()) {
/// (l1, l2, m1, m2) if l1 == m1 && l2 == m2 => {},
/// (l2, l1, m1, m2) if l1 == m1 && l2 == m2 => {
/// std::mem::swap(&mut time1, &mut time2);
/// std::mem::swap(&mut buffer1, &mut buffer2);
/// },
/// _ => panic!("Wrong packets"),
/// };
///
/// // Compare results
/// println!("{:?} @ {:?}, {:?} @ {:?}, {:?}", buffer1, time1, buffer2, time2, delay);
/// assert!(message1 == &buffer1[0..(message1.len())], "{:?} == {:?}", message1, buffer1);
/// assert!(message2 == &buffer2[0..(message2.len())], "{:?} == {:?}", message2, buffer2);
/// let time = time2 - time1;
/// let time = Duration::new(time.num_seconds() as u64, time.num_nanoseconds() as u32);
/// let difference = if delay < time { time - delay } else { delay - time };
/// assert!(difference.subsec_nanos() < 5_000_000, "{}ns < 5ms", difference.subsec_nanos());
/// assert!(difference.as_secs() == 0);
///
/// // Close socket
/// nix::unistd::close(in_socket).unwrap();
/// ```
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
ScmTimestamp(&'a ::sys::time::TimeVal),
#[doc(hidden)]
Unknown(UnknownCmsg<'a>),
}
Expand All @@ -303,6 +375,10 @@ impl<'a> ControlMessage<'a> {
ControlMessage::ScmRights(fds) => {
mem::size_of_val(fds)
},
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
ControlMessage::ScmTimestamp(t) => {
mem::size_of_val(t)
},
ControlMessage::Unknown(UnknownCmsg(_, bytes)) => {
mem::size_of_val(bytes)
}
Expand Down Expand Up @@ -333,6 +409,26 @@ impl<'a> ControlMessage<'a> {

copy_bytes(fds, buf);
},
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
ControlMessage::ScmTimestamp(t) => {
let cmsg = cmsghdr {
cmsg_len: self.len() as type_of_cmsg_len,
cmsg_level: libc::SOL_SOCKET,
cmsg_type: libc::SCM_TIMESTAMP,
cmsg_data: [],
};
copy_bytes(&cmsg, buf);

let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
mem::size_of_val(&cmsg);

let mut tmpbuf = &mut [][..];
mem::swap(&mut tmpbuf, buf);
let (_padding, mut remainder) = tmpbuf.split_at_mut(padlen);
mem::swap(buf, &mut remainder);

copy_bytes(t, buf);
},
ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => {
copy_bytes(orig_cmsg, buf);
copy_bytes(bytes, buf);
Expand Down
2 changes: 2 additions & 0 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ sockopt_impl!(GetOnly, SockType, libc::SOL_SOCKET, libc::SO_TYPE, super::SockTyp
sockopt_impl!(GetOnly, AcceptConn, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
#[cfg(any(target_os = "linux", target_os = "android"))]
sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
#[cfg(not(all(target_arch = "x86", target_os = "freebsd")))]
sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);

/*
*
Expand Down

0 comments on commit 8a7c977

Please sign in to comment.