Skip to content

Commit 746309c

Browse files
committed
Add support for SO_TIMESTAMP
1 parent be7acc1 commit 746309c

File tree

3 files changed

+97
-2
lines changed

3 files changed

+97
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1212
([#672](https://github.com/nix-rust/nix/pull/672))
1313
- Added protocol families in `AddressFamily` enum.
1414
([#647](https://github.com/nix-rust/nix/pull/647))
15+
- Added timestamp socket control message variant:
16+
`nix::sys::socket::ControlMessage::ScmTimestamp`
17+
([#663](https://github.com/nix-rust/nix/pull/663))
18+
- Added socket option variant that enables the timestamp socket
19+
control message: `nix::sys::socket::sockopt::ReceiveTimestamp`
20+
([#663](https://github.com/nix-rust/nix/pull/663))
1521

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

src/sys/socket/mod.rs

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use libc::{self, c_void, c_int, socklen_t, size_t, pid_t, uid_t, gid_t};
99
use std::{mem, ptr, slice};
1010
use std::os::unix::io::RawFd;
1111
use sys::uio::IoVec;
12+
use sys::time::TimeVal;
1213

1314
mod addr;
1415
mod ffi;
@@ -245,6 +246,10 @@ impl<'a> Iterator for CmsgIterator<'a> {
245246
slice::from_raw_parts(
246247
&cmsg.cmsg_data as *const _ as *const _, 1)))
247248
},
249+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => unsafe {
250+
Some(ControlMessage::ScmTimestamp(
251+
&*(&cmsg.cmsg_data as *const _ as *const _)))
252+
},
248253
(_, _) => unsafe {
249254
Some(ControlMessage::Unknown(UnknownCmsg(
250255
&cmsg,
@@ -261,10 +266,71 @@ impl<'a> Iterator for CmsgIterator<'a> {
261266
/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html)
262267
pub enum ControlMessage<'a> {
263268
/// A message of type SCM_RIGHTS, containing an array of file
264-
/// descriptors passed between processes. See the description in the
265-
/// "Ancillary messages" section of the
269+
/// descriptors passed between processes.
270+
///
271+
/// See the description in the "Ancillary messages" section of the
266272
/// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html).
267273
ScmRights(&'a [RawFd]),
274+
/// A message of type SCM_TIMESTAMP, containing the time the packet
275+
/// was received by the kernel.
276+
///
277+
/// See the kernel's explanation in "SO_TIMESTAMP" of
278+
/// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
279+
///
280+
/// # Example
281+
///
282+
/// ```
283+
/// use nix::sys::socket::*;
284+
/// use nix::sys::uio::IoVec;
285+
/// use nix::sys::time::*;
286+
///
287+
/// // Set up
288+
/// let delay = 250;
289+
/// let message1 = "Ohayō!".as_bytes();
290+
/// let message2 = "Jā ne".as_bytes();
291+
/// let in_socket = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
292+
/// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
293+
/// bind(in_socket, &SockAddr::new_inet(InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0))).unwrap();
294+
/// let address = if let Ok(address) = getsockname(in_socket) { address } else { unreachable!() };
295+
///
296+
/// // Send both
297+
/// assert!(Ok(message1.len()) == sendmsg(in_socket, &[IoVec::from_slice(message1)], &[], MsgFlags::empty(), Some(&address)));
298+
/// std::thread::sleep(std::time::Duration::from_millis(delay as u64));
299+
/// assert!(Ok(message2.len()) == sendmsg(in_socket, &[IoVec::from_slice(message2)], &[], MsgFlags::empty(), Some(&address)));
300+
///
301+
/// // Receive the first
302+
/// let mut buffer1 = vec![0u8; message1.len() + message2.len()];
303+
/// let mut time1: CmsgSpace<TimeVal> = CmsgSpace::new();
304+
/// let received1 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer1)], Some(&mut time1), MsgFlags::empty()).unwrap();
305+
/// let mut time1 = if let Some(ControlMessage::ScmTimestamp(&time1)) = received1.cmsgs().next() { time1 } else { panic!("Unexpected or no control message") };
306+
///
307+
/// // Receive the second
308+
/// let mut buffer2 = vec![0u8; message1.len() + message2.len()];
309+
/// let mut time2: CmsgSpace<TimeVal> = CmsgSpace::new();
310+
/// let received2 = recvmsg(in_socket, &[IoVec::from_mut_slice(&mut buffer2)], Some(&mut time2), MsgFlags::empty()).unwrap();
311+
/// let mut time2 = if let Some(ControlMessage::ScmTimestamp(&time2)) = received2.cmsgs().next() { time2 } else { panic!("Unexpected or no control message") };
312+
///
313+
/// // Swap if needed, UDP is unordered
314+
/// match (received1.bytes, received2.bytes, message1.len(), message2.len()) {
315+
/// (l1, l2, m1, m2) if l1 == m1 && l2 == m2 => {},
316+
/// (l2, l1, m1, m2) if l1 == m1 && l2 == m2 =>
317+
/// std::mem::swap(&mut (&mut time1, &mut buffer1), &mut(&mut time2, &mut buffer2)),
318+
/// _ => panic!("Wrong packets"),
319+
/// };
320+
///
321+
/// // Compare results
322+
/// assert!(message1 == &buffer1[0..(message1.len())], "{:?} == {:?}", message1, buffer1);
323+
/// assert!(message2 == &buffer2[0..(message2.len())], "{:?} == {:?}", message2, buffer2);
324+
/// let time = time2 - time1;
325+
/// assert!(time.num_milliseconds() < delay + 10, "{} < {}", time.num_milliseconds(), delay + 10);
326+
/// assert!(time.num_milliseconds() > delay - 10, "{} < {}", time.num_milliseconds(), delay - 10);
327+
/// assert!(time.num_seconds() == 0);
328+
/// println!("{:?} @ {:?}, {:?} @ {:?}", buffer1, time1, buffer2, time2);
329+
///
330+
/// // Close socket
331+
/// nix::unistd::close(in_socket).unwrap();
332+
/// ```
333+
ScmTimestamp(&'a TimeVal),
268334
#[doc(hidden)]
269335
Unknown(UnknownCmsg<'a>),
270336
}
@@ -290,6 +356,9 @@ impl<'a> ControlMessage<'a> {
290356
ControlMessage::ScmRights(fds) => {
291357
mem::size_of_val(fds)
292358
},
359+
ControlMessage::ScmTimestamp(t) => {
360+
mem::size_of_val(t)
361+
},
293362
ControlMessage::Unknown(UnknownCmsg(_, bytes)) => {
294363
mem::size_of_val(bytes)
295364
}
@@ -320,6 +389,25 @@ impl<'a> ControlMessage<'a> {
320389

321390
copy_bytes(fds, buf);
322391
},
392+
ControlMessage::ScmTimestamp(t) => {
393+
let cmsg = cmsghdr {
394+
cmsg_len: self.len() as type_of_cmsg_len,
395+
cmsg_level: libc::SOL_SOCKET,
396+
cmsg_type: libc::SCM_TIMESTAMP,
397+
cmsg_data: [],
398+
};
399+
copy_bytes(&cmsg, buf);
400+
401+
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
402+
mem::size_of_val(&cmsg);
403+
404+
let mut tmpbuf = &mut [][..];
405+
mem::swap(&mut tmpbuf, buf);
406+
let (_padding, mut remainder) = tmpbuf.split_at_mut(padlen);
407+
mem::swap(buf, &mut remainder);
408+
409+
copy_bytes(t, buf);
410+
},
323411
ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => {
324412
copy_bytes(orig_cmsg, buf);
325413
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)