statime_netptp/interface.rs
1use std::{collections::hash_map::Entry, sync::Arc};
2
3use timestamped_socket::interface::InterfaceName;
4
5use crate::{
6 ConnectedSocket, NetworkManagerData, OpenSocket, PACKET_BUFFER_SIZE, PtpAddressFamily,
7 SocketData, TimestampSource,
8};
9
10/// A handle for a network interface
11///
12/// Describes the configured network interface and way of timestamping for its
13/// sockets. It also ensures the requested timestamping clock remains available
14/// on the interface during its existence.
15pub struct Interface<A: PtpAddressFamily> {
16 pub(crate) state: Arc<NetworkManagerData<A>>,
17 pub(crate) name: Option<InterfaceName>,
18 pub(crate) timestamp_source: TimestampSource,
19}
20
21impl<A: PtpAddressFamily> Clone for Interface<A> {
22 fn clone(&self) -> Self {
23 // The mutex can only be poisoned from an earlier panic. It is ok for
24 // us to propagate that to all the threads.
25 let mut interfaces = self.state.interfaces.write().unwrap();
26 // The interface will always be present because of the refcounting.
27 let entry = interfaces.get_mut(&self.name).unwrap();
28 // An overflowing reference count is an error condition from which we
29 // cannot reasonably recover. A panic here is the best solution.
30 entry.refcount = entry.refcount.checked_add(1).unwrap();
31 Self {
32 state: self.state.clone(),
33 name: self.name,
34 timestamp_source: self.timestamp_source,
35 }
36 }
37}
38
39impl<A: PtpAddressFamily> Drop for Interface<A> {
40 fn drop(&mut self) {
41 // The mutex can only be poisoned from an earlier panic. It is ok for
42 // us to propagate that to all the threads.
43 let mut interfaces = self.state.interfaces.write().unwrap();
44 // The interface will always be present because of the refcounting.
45 let entry = interfaces.get_mut(&self.name).unwrap();
46 // An underflowing refcount should never happen. If it does, something
47 // is seriously wrong and aborting is the best solution.
48 entry.refcount = entry.refcount.checked_sub(1).unwrap();
49 if entry.refcount == 0 {
50 interfaces.remove(&self.name);
51 }
52 }
53}
54
55impl<A: PtpAddressFamily> Interface<A> {
56 /// Create an open socket intended for listening to the network.
57 ///
58 /// Creates a socket that is intended to receive traffic from any remote,
59 /// potentially providing responses to that traffic.
60 #[must_use]
61 #[expect(
62 clippy::missing_panics_doc,
63 reason = "Function will only panic if there is an implementation bug in this crate."
64 )]
65 pub fn listen_socket(&self) -> OpenSocket<A> {
66 let (socket_channel_tx, socket_channel_rx) = tokio::sync::mpsc::channel(PACKET_BUFFER_SIZE);
67
68 let socket = SocketData {
69 remote_filter: None,
70 local_filter: None,
71 interface_filter: self.name,
72 timestamp_source: self.timestamp_source,
73 message_channel: socket_channel_tx,
74 };
75
76 {
77 // The mutex can only be poisoned from an earlier panic. It is ok for
78 // us to propagate that to all the threads.
79 let mut interfaces = self.state.interfaces.write().unwrap();
80 // The interface will always be present because of the refcounting.
81 let entry = interfaces.get_mut(&self.name).unwrap();
82 // An overflowing reference count is an error condition from which we
83 // cannot reasonably recover. A panic here is the best solution.
84 entry.refcount = entry.refcount.checked_add(1).unwrap();
85 }
86
87 let socket_id = {
88 // The mutex can only be poisoned from an earlier panic. It is ok for
89 // us to propagate that to all the threads.
90 let mut sockets = self.state.sockets.write().unwrap();
91 loop {
92 let socket_id = self
93 .state
94 .next_socket_id
95 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
96 if let Entry::Vacant(vacant_entry) = sockets.entry(socket_id) {
97 vacant_entry.insert(socket);
98 break socket_id;
99 }
100 }
101 };
102
103 OpenSocket {
104 state: self.state.clone(),
105 socket_id,
106 packet_receiver: socket_channel_rx,
107 }
108 }
109
110 /// Create a socket connected to a specific remote address.
111 ///
112 /// This creates a socket intended for use in communicating with a single
113 /// remote party over the interface. The `remote` address specifies the
114 /// network address of that party.
115 ///
116 /// The `local` address specifies which address should be used as the
117 /// sender address on the messages. If this is not specified, the operating
118 /// system will select a send address automatically.
119 #[expect(
120 clippy::missing_panics_doc,
121 reason = "Function will only panic if there is an implementation bug in this crate."
122 )]
123 pub fn connected_socket(&self, local: Option<A>, remote: A) -> ConnectedSocket<A> {
124 let (socket_channel_tx, socket_channel_rx) = tokio::sync::mpsc::channel(PACKET_BUFFER_SIZE);
125
126 let socket = SocketData {
127 remote_filter: Some(remote),
128 local_filter: local,
129 interface_filter: self.name,
130 timestamp_source: self.timestamp_source,
131 message_channel: socket_channel_tx,
132 };
133
134 {
135 // The mutex can only be poisoned from an earlier panic. It is ok for
136 // us to propagate that to all the threads.
137 let mut interfaces = self.state.interfaces.write().unwrap();
138 // The interface will always be present because of the refcounting.
139 let entry = interfaces.get_mut(&self.name).unwrap();
140 // An overflowing reference count is an error condition from which we
141 // cannot reasonably recover. A panic here is the best solution.
142 entry.refcount = entry.refcount.checked_add(1).unwrap();
143 }
144
145 let socket_id = {
146 // The mutex can only be poisoned from an earlier panic. It is ok for
147 // us to propagate that to all the threads.
148 let mut sockets = self.state.sockets.write().unwrap();
149 loop {
150 let socket_id = self
151 .state
152 .next_socket_id
153 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
154 if let Entry::Vacant(vacant_entry) = sockets.entry(socket_id) {
155 vacant_entry.insert(socket);
156 break socket_id;
157 }
158 }
159 };
160
161 ConnectedSocket {
162 state: self.state.clone(),
163 socket_id,
164 packet_receiver: socket_channel_rx,
165 }
166 }
167}