-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlib.rs
More file actions
375 lines (341 loc) · 10.9 KB
/
Copy pathlib.rs
File metadata and controls
375 lines (341 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
//! This is a crate for interacting with layer-2 network devices through DLPI.
//! For more details on DLPI see `man dlpi` and `man libdlpi`.
//!
//! ```no_run
//! use std::io::Result;
//! use std::thread::spawn;
//!
//! fn main() -> Result<()> {
//! // L2 multicast address to send packets to
//! let mc = [0xff, 0xff, 0x00, 0x00, 0x00, 0x47];
//!
//! // Open up an interface called sim0 and attach to the ethertype 0x4000
//! let dh_recv = dlpi::open("sim0", 0).expect("open recv");
//! dlpi::bind(dh_recv, 0x4000).expect("bind recv");
//! dlpi::enable_multicast(dh_recv, &mc).expect("enable multicast");
//!
//! // strt a receiver thread
//! let t = spawn(move || {
//!
//! // allocated buffers for a senders L2 address and a message
//! let mut src = [0u8; dlpi::sys::DLPI_PHYSADDR_MAX];
//! let mut msg = [0; 256];
//!
//! // wait for a message
//! let n = match dlpi::recv(dh_recv, &mut src, &mut msg, -1, None) {
//! Ok((_, len)) => len,
//! Err(e) => panic!("recv: {}", e),
//! };
//!
//! });
//!
//! // Open up an interface called sim1 and attach to ethertype 0x4000
//! let dh_send = dlpi::open("sim1", 0).expect("send");
//! dlpi::bind(dh_send, 0x4000).expect("bind");
//!
//! // Send a message
//! let message = b"do you know the muffin man?";
//! dlpi::send(dh_send, &mc, &message[..], None).expect("send");
//!
//! // wait on the receiver and then shut down
//! t.join().expect("join recv thread");
//! dlpi::close(dh_send);
//! dlpi::close(dh_recv);
//!
//! Ok(())
//! }
//! ```
use num_enum::TryFromPrimitive;
#[cfg(feature = "async")]
use std::future::Future;
use std::io::{Error, ErrorKind, Result};
use std::os::raw::{c_char, c_void};
#[cfg(feature = "async")]
use std::pin::Pin;
use std::ptr;
#[cfg(feature = "async")]
use std::task::{Context, Poll};
use thiserror::Error;
pub use libdlpi_sys as sys;
/// Result of a DLPI operation.
#[repr(i32)]
#[derive(PartialEq, Eq, Error, Debug, Copy, Clone, TryFromPrimitive)]
pub enum ResultCode {
#[error("success")]
Success = 10000, /* DLPI operation succeeded */
#[error("invalid argument")]
EInval, /* invalid argument */
#[error("invalid link name")]
ELinkNameInval, /* invalid DLPI linkname */
#[error("link does not exist")]
ENoLink, /* DLPI link does not exist */
#[error("bad link")]
EBadLink, /* bad DLPI link */
#[error("invalid handle")]
EInHandle, /* invalid DLPI handle */
#[error("operation timed out")]
ETimedout, /* DLPI operation timed out */
#[error("unsupported version")]
EVerNotSup, /* unsupported DLPI Version */
#[error("unsupported connection mode")]
EModeNotSup, /* unsupported DLPI connection mode */
#[error("unavailable service access point")]
EUnavailSAP, /* unavailable DLPI SAP */
#[error("failure")]
Failure, /* DLPI operation failed */
#[error("style-2 node reports style-1")]
ENotStyle2, /* DLPI style-2 node reports style-1 */
#[error("bad message")]
EBadMsg, /* bad DLPI message */
#[error("raw mode not supported")]
ERawNotSup, /* DLPI raw mode not supported */
#[error("invalid notification type")]
ENoteInval, /* invalid DLPI notification type */
#[error("notification not supported by link")]
ENoteNotSup, /* DLPI notification not supported by link */
#[error("invalid notification id")]
ENoteIdInval, /* invalid DLPI notification id */
#[error("ipnetinfo not supported")]
EIpNetInfoNotSup, /* DLPI_IPNETINFO not supported */
#[error("error max")]
ErrMax, /* Highest + 1 libdlpi error code */
}
/// A DLPI handle wrapper that implements `Send` and `Sync`.
#[derive(Clone, Copy)]
pub struct DlpiHandle(pub *mut sys::dlpi_handle_t);
unsafe impl Send for DlpiHandle {}
unsafe impl Sync for DlpiHandle {}
/// A wrapper for DlpiHandle that closes the DLPI instance when dropped.
pub struct DropHandle(pub DlpiHandle);
impl Drop for DropHandle {
/// Closes underlying DLPI instance.
fn drop(&mut self) {
close(self.0);
}
}
impl DropHandle {
/// Get the filesystem descriptor associated with this drop handle.
pub fn fd(&self) -> Result<i32> {
fd(self.0)
}
}
/// Creates a DLPI link instance.
pub fn open(linkname: impl AsRef<str>, flags: u32) -> Result<DlpiHandle> {
let linkname = format!("{}\0", linkname.as_ref());
let mut dhp = sys::null_dlpi_handle();
let ret = unsafe {
sys::dlpi_open(
linkname.as_str().as_ptr() as *const c_char,
&mut dhp,
flags,
)
};
check_return(ret)?;
Ok(DlpiHandle(dhp))
}
/// Send a message over a DLPI link.
pub fn send(
h: DlpiHandle,
dst: &[u8],
msg: &[u8],
info: Option<&sys::dlpi_sendinfo_t>,
) -> Result<()> {
let ret = unsafe {
sys::dlpi_send(
h.0,
dst.as_ptr() as *const c_void,
dst.len(),
msg.as_ptr() as *const c_void,
msg.len(),
match info {
Some(info) => info as *const sys::dlpi_sendinfo_t,
None => ptr::null(),
},
)
};
check_return(ret)?;
Ok(())
}
/// Receive a message from a DLPI link.
///
/// Data is placed into provided buffer. Return values is (address bytes read,
/// message bytes read).
///
/// If no message is received within `msec` milliseconds, returns
/// [`ResultCode::ETimedout`].
///
/// **`src` must be at least [`sys::DLPI_PHYSADDR_MAX`] in length**.
pub fn recv(
h: DlpiHandle,
src: &mut [u8],
msg: &mut [u8],
msec: i32,
info: Option<&mut sys::dlpi_recvinfo_t>,
) -> Result<(usize, usize)> {
let mut src_read = src.len();
let mut msg_read = msg.len();
let ret = unsafe {
sys::dlpi_recv(
h.0,
src.as_mut_ptr() as *mut c_void,
&mut src_read,
msg.as_mut_ptr() as *mut c_void,
&mut msg_read,
msec,
match info {
Some(info) => info as *mut sys::dlpi_recvinfo_t,
None => ptr::null_mut(),
},
)
};
check_return(ret)?;
Ok((src_read, msg_read))
}
#[cfg(feature = "async")]
/// A receiver object returned from [`recv_async`] wrapped in a future. Calling
/// `await` on this object yields the same result as [`recv`].
pub struct DlpiRecv<'a> {
h: DlpiHandle,
src: &'a mut [u8],
msg: &'a mut [u8],
info: Option<&'a mut sys::dlpi_recvinfo_t>,
}
/// An `async` version of [`recv`]. Calling `await` on result yields same
/// result as [`recv`].
///
/// **`src` must be at least [`sys::DLPI_PHYSADDR_MAX`] in length**.
/*pub fn recv_async<'a>(
h: DlpiHandle,
src: &'a mut [u8],
msg: &'a mut [u8],
info: Option<&'a mut sys::dlpi_recvinfo_t>,
) -> DlpiRecv<'a> {
DlpiRecv::<'a> { h, src, msg, info }
}
*/
#[cfg(feature = "async")]
pub async fn recv_async<'a>(
h: DlpiHandle,
src: &'a mut [u8],
msg: &'a mut [u8],
info: Option<&'a mut sys::dlpi_recvinfo_t>,
) -> Result<(usize, usize)> {
let afd = tokio::io::unix::AsyncFd::new(fd(h)?)?;
let mut _guard = afd.readable().await?;
recv(
h, src, msg, 0, // non blocking
info,
)
}
#[cfg(feature = "async")]
impl<'a> Future for DlpiRecv<'a> {
type Output = Result<(usize, usize)>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut src_read = self.src.len();
let mut msg_read = self.msg.len();
let s = self.get_mut();
let ret = unsafe {
sys::dlpi_recv(
s.h.0,
s.src.as_mut_ptr() as *mut c_void,
&mut src_read,
s.msg.as_mut_ptr() as *mut c_void,
&mut msg_read,
0, // non blocking
match s.info {
Some(ref mut info) => *info as *mut sys::dlpi_recvinfo_t,
None => ptr::null_mut(),
},
)
};
if ret == ResultCode::Success as i32 {
Poll::Ready(Ok((src_read, msg_read)))
} else if ret == ResultCode::ETimedout as i32 {
cx.waker().wake_by_ref();
Poll::Pending
} else {
Poll::Ready(Err(to_io_error(ret)))
}
}
}
/// Bind a DLPI link to a service access point type.
///
/// This will restrict the DLPI link to only operate on the provided service
/// access point. For ethernet the service access point is the ethertype.
pub fn bind(h: DlpiHandle, sap: u32) -> Result<u32> {
let mut bound_sap = 0;
let ret = unsafe { sys::dlpi_bind(h.0, sap, &mut bound_sap) };
check_return(ret)?;
Ok(bound_sap)
}
/// Enable reception of messages destined to the provided layer-2 address.
///
/// In most cases the layer 2 address will be a mac address. For example,
/// something in the range `33:33:00:00:00:00`-`33:33:FF:FF:FF:FF` for IPv6
/// multicast.
pub fn enable_multicast(h: DlpiHandle, addr: &[u8]) -> Result<()> {
let ret = unsafe {
sys::dlpi_enabmulti(h.0, addr.as_ptr() as *const c_void, addr.len())
};
check_return(ret)?;
Ok(())
}
/// Disable reception of messages destined to the provided layer-2 address.
///
/// In most cases the layer 2 address will be a mac address. For example,
/// something in the range `33:33:00:00:00:00`-`33:33:FF:FF:FF:FF` for IPv6
/// multicast.
pub fn disable_multicast(h: DlpiHandle, addr: &[u8]) -> Result<()> {
let ret = unsafe {
sys::dlpi_disabmulti(h.0, addr.as_ptr() as *const c_void, addr.len())
};
check_return(ret)?;
Ok(())
}
/// Enable promiscuous mode for the specified handle. See DL_PROMISC_* for
/// levels.
pub fn promisc_on(h: DlpiHandle, level: u32) -> Result<()> {
let ret = unsafe { sys::dlpi_promiscon(h.0, level) };
match ret {
-1 => Err(Error::from_raw_os_error(libc::EINVAL)),
_ => Ok(()),
}
}
/// Disable promiscuous mode for the specified handle. See DL_PROMISC_* for
/// levels.
pub fn promisc_off(h: DlpiHandle, level: u32) -> Result<()> {
let ret = unsafe { sys::dlpi_promiscoff(h.0, level) };
match ret {
-1 => Err(Error::from_raw_os_error(libc::EINVAL)),
_ => Ok(()),
}
}
/// Get a file descriptor associated with the provided handle.
pub fn fd(h: DlpiHandle) -> Result<i32> {
let ret = unsafe { sys::dlpi_fd(h.0) };
match ret {
-1 => Err(Error::from_raw_os_error(libc::EINVAL)),
_ => Ok(ret),
}
}
/// Close the provided handle.
pub fn close(h: DlpiHandle) {
unsafe { sys::dlpi_close(h.0) };
}
fn check_return(ret: i32) -> Result<()> {
if ret == ResultCode::Success as i32 {
return Ok(());
}
Err(to_io_error(ret))
}
fn to_io_error(ret: i32) -> Error {
if ret == sys::DL_SYSERR {
return Error::last_os_error();
}
match ResultCode::try_from(ret) {
Ok(rc) => Error::new(ErrorKind::Other, rc),
Err(_) => Error::from_raw_os_error(ret),
}
}
#[cfg(test)]
mod test;