ntp_udp/
hwtimestamp.rs

1use std::os::unix::io::AsRawFd;
2
3use crate::{interface::InterfaceName, raw_socket::cerr};
4
5const fn standard_hwtstamp_config() -> libc::hwtstamp_config {
6    libc::hwtstamp_config {
7        flags: 0,
8        tx_type: libc::HWTSTAMP_TX_ON as _,
9        rx_filter: libc::HWTSTAMP_FILTER_ALL as _,
10    }
11}
12
13pub fn driver_enable_hardware_timestamping(
14    udp_socket: &std::net::UdpSocket,
15) -> std::io::Result<()> {
16    set_hardware_timestamp(udp_socket, standard_hwtstamp_config())
17}
18
19fn set_hardware_timestamp(
20    udp_socket: &std::net::UdpSocket,
21    mut config: libc::hwtstamp_config,
22) -> std::io::Result<()> {
23    let mut ifreq: libc::ifreq = libc::ifreq {
24        ifr_name: socket_interface_name(udp_socket)?,
25        ifr_ifru: libc::__c_anonymous_ifr_ifru {
26            ifru_data: (&mut config as *mut _) as *mut libc::c_char,
27        },
28    };
29
30    let fd = udp_socket.as_raw_fd();
31    cerr(unsafe { libc::ioctl(fd, libc::SIOCSHWTSTAMP as _, &mut ifreq) })?;
32
33    Ok(())
34}
35
36#[allow(unused)]
37fn get_hardware_timestamp(
38    udp_socket: &std::net::UdpSocket,
39) -> std::io::Result<libc::hwtstamp_config> {
40    let mut tstamp_config = libc::hwtstamp_config {
41        flags: 0,
42        tx_type: 0,
43        rx_filter: 0,
44    };
45
46    let mut ifreq: libc::ifreq = libc::ifreq {
47        ifr_name: socket_interface_name(udp_socket)?,
48        ifr_ifru: libc::__c_anonymous_ifr_ifru {
49            ifru_data: (&mut tstamp_config as *mut _) as *mut libc::c_char,
50        },
51    };
52
53    let fd = udp_socket.as_raw_fd();
54    cerr(unsafe { libc::ioctl(fd, libc::SIOCGHWTSTAMP as _, &mut ifreq) })?;
55
56    Ok(tstamp_config)
57}
58
59fn socket_interface_name(
60    udp_socket: &std::net::UdpSocket,
61) -> std::io::Result<[libc::c_char; libc::IFNAMSIZ]> {
62    use std::io::{Error, ErrorKind};
63
64    match InterfaceName::from_socket_addr(udp_socket.local_addr()?)? {
65        Some(interface_name) => Ok(interface_name.to_ifr_name()),
66        None => Err(Error::new(ErrorKind::Other, "socket has no interface name")),
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn get_hwtimestamp() -> std::io::Result<()> {
76        let udp_socket = std::net::UdpSocket::bind(("0.0.0.0", 9000))?;
77        udp_socket.connect(("10.0.0.18", 9001))?;
78
79        if let Err(e) = get_hardware_timestamp(&udp_socket) {
80            assert!(
81                e.to_string().contains("Operation not supported")
82                    || e.to_string().contains("Not supported")
83            );
84        }
85
86        Ok(())
87    }
88
89    #[test]
90    #[ignore = "requires elevated permissions to run"]
91    fn get_set_hwtimestamp() -> std::io::Result<()> {
92        let udp_socket = std::net::UdpSocket::bind(("0.0.0.0", 9002))?;
93        udp_socket.connect(("10.0.0.18", 9003))?;
94
95        let old = get_hardware_timestamp(&udp_socket)?;
96
97        let custom = standard_hwtstamp_config();
98
99        set_hardware_timestamp(&udp_socket, custom)?;
100        let new = get_hardware_timestamp(&udp_socket)?;
101
102        let custom = standard_hwtstamp_config();
103        assert_eq!(new.flags, custom.flags);
104        assert_eq!(new.tx_type, custom.tx_type);
105        assert_eq!(new.rx_filter, custom.rx_filter);
106
107        set_hardware_timestamp(&udp_socket, old)?;
108
109        Ok(())
110    }
111}