timestamped_socket/interface/
linux.rs1use std::{array, io::ErrorKind, os::fd::RawFd};
2
3use libc::{
4 __c_anonymous_ifr_ifru, close, ifreq, ioctl, recv, socket, AF_INET, SIOCETHTOOL, SOCK_DGRAM,
5};
6use tokio::io::{unix::AsyncFd, Interest};
7
8use crate::{cerr, control_message::zeroed_sockaddr_storage};
9
10use super::InterfaceName;
11
12#[repr(C)]
13struct EthtoolTsInfo {
14 cmd: u32,
15 so_timestamping: u32,
16 phc_index: i32,
17 tx_types: u32,
18 reserved1: [u32; 3],
19 rx_filters: u32,
20 reserved2: [u32; 3],
21}
22
23const ETHTOOL_GET_TS_INFO: u32 = 0x41;
24
25pub fn lookup_phc(interface: InterfaceName) -> Option<u32> {
26 let fd = unsafe { socket(AF_INET, SOCK_DGRAM, 0) };
29 if fd < 0 {
30 tracing::error!("Could not open socket for looking up PHC index");
31 return None;
32 }
33
34 let mut ethtool_ts_info = EthtoolTsInfo {
35 cmd: ETHTOOL_GET_TS_INFO,
36 so_timestamping: 0,
37 phc_index: -1,
38 tx_types: 0,
39 reserved1: [0; 3],
40 rx_filters: 0,
41 reserved2: [0; 3],
42 };
43
44 let mut request = ifreq {
45 ifr_name: array::from_fn(|i| interface.bytes[i] as _),
46 ifr_ifru: __c_anonymous_ifr_ifru {
47 ifru_data: &mut ethtool_ts_info as *mut _ as *mut _,
48 },
49 };
50
51 let error = unsafe {
53 ioctl(
54 fd,
55 SIOCETHTOOL as _,
56 &mut request as *mut _ as *mut libc::c_void,
57 )
58 };
59
60 unsafe {
63 close(fd);
64 }
65
66 if error < 0 {
67 None
68 } else if ethtool_ts_info.phc_index >= 0 {
69 Some(ethtool_ts_info.phc_index as u32)
70 } else {
71 None
72 }
73}
74
75pub struct ChangeDetector {
76 fd: AsyncFd<RawFd>,
77}
78
79impl ChangeDetector {
80 pub fn new() -> std::io::Result<Self> {
81 const _: () = assert!(
82 std::mem::size_of::<libc::sockaddr_storage>()
83 >= std::mem::size_of::<libc::sockaddr_nl>()
84 );
85 const _: () = assert!(
86 std::mem::align_of::<libc::sockaddr_storage>()
87 >= std::mem::align_of::<libc::sockaddr_nl>()
88 );
89
90 let mut address_buf = zeroed_sockaddr_storage();
91 let address = unsafe {
95 &mut *(&mut address_buf as *mut libc::sockaddr_storage as *mut libc::sockaddr_nl)
96 };
97
98 address.nl_family = libc::AF_NETLINK as _;
99 address.nl_groups =
100 (libc::RTMGRP_IPV4_IFADDR | libc::RTMGRP_IPV6_IFADDR | libc::RTMGRP_LINK) as _;
101
102 let fd =
104 cerr(unsafe { libc::socket(libc::AF_NETLINK, libc::SOCK_RAW, libc::NETLINK_ROUTE) })?;
105 cerr(unsafe {
107 libc::bind(
108 fd,
109 address as *mut _ as *mut _,
110 std::mem::size_of_val(address) as _,
111 )
112 })?;
113
114 let nonblocking = 1 as libc::c_int;
115 cerr(unsafe { libc::ioctl(fd, libc::FIONBIO, &nonblocking) })?;
117
118 Ok(ChangeDetector {
119 fd: AsyncFd::new(fd)?,
120 })
121 }
122
123 fn empty(fd: i32) {
124 loop {
125 let mut buf = [0u8; 16];
127 match cerr(unsafe {
128 recv(
129 fd,
130 &mut buf as *mut _ as *mut _,
131 std::mem::size_of_val(&buf) as _,
132 0,
133 ) as _
134 }) {
135 Ok(_) => continue,
136 Err(e) if e.kind() == ErrorKind::WouldBlock => break,
137 Err(e) => {
138 tracing::error!("Could not receive on change socket: {}", e);
139 break;
140 }
141 }
142 }
143 }
144
145 pub async fn wait_for_change(&mut self) {
146 if let Err(e) = self
147 .fd
148 .async_io(Interest::READABLE, |fd| {
149 let mut buf = [0u8; 16];
151 cerr(unsafe {
152 recv(
153 *fd,
154 &mut buf as *mut _ as *mut _,
155 std::mem::size_of_val(&buf) as _,
156 0,
157 ) as _
158 })?;
159 Self::empty(*fd);
160 Ok(())
161 })
162 .await
163 {
164 tracing::error!("Could not receive on change socket: {}", e);
165 }
166 }
167}