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}