socketcan_rs/
socket.rs

1//! from [socketcan](https://crates.io/crates/socketcan-rs)
2
3use libc::*;
4use std::{
5    ffi::CString,
6    fmt, io, mem,
7    os::raw::{c_int, c_void},
8    ptr,
9    time::Duration,
10};
11
12/// Tries to open the CAN socket by the interface number.
13pub fn raw_open_socket(addr: &CanAddr) -> io::Result<c_int> {
14    let fd = unsafe { socket(PF_CAN, SOCK_RAW, CAN_RAW) };
15
16    if fd == -1 {
17        return Err(io::Error::last_os_error());
18    }
19
20    let ret = unsafe { bind(fd, addr.as_sockaddr_ptr(), CanAddr::len() as u32) };
21
22    if ret == -1 {
23        let err = io::Error::last_os_error();
24        unsafe { close(fd) };
25        Err(err)
26    } else {
27        Ok(fd)
28    }
29}
30
31// Enable or disable FD mode on the socket, fd.
32pub fn set_fd_mode(fd: c_int, enable: bool) -> io::Result<c_int> {
33    let enable = enable as c_int;
34
35    let ret = unsafe {
36        setsockopt(
37            fd,
38            SOL_CAN_RAW,
39            CAN_RAW_FD_FRAMES,
40            &enable as *const _ as *const c_void,
41            mem::size_of::<c_int>() as u32,
42        )
43    };
44
45    if ret == -1 {
46        Err(io::Error::last_os_error())
47    } else {
48        Ok(fd)
49    }
50}
51
52// Write a single frame of any type to the socket, fd.
53pub fn raw_write_frame<T>(fd: c_int, frame_ptr: *const T, n: usize) -> io::Result<()> {
54    let ret = unsafe { write(fd, frame_ptr.cast(), n) };
55
56    if ret as usize == n {
57        Ok(())
58    } else {
59        Err(io::Error::last_os_error())
60    }
61}
62
63/// `setsockopt` wrapper
64///
65/// The libc `setsockopt` function is set to set various options on a socket.
66/// `set_socket_option` offers a somewhat type-safe wrapper that does not
67/// require messing around with `*const c_void`s.
68///
69/// A proper `std::io::Error` will be returned on failure.
70///
71/// Example use:
72///
73/// ```text
74/// let fd = ...;  // some file descriptor, this will be stdout
75/// set_socket_option(fd, SOL_TCP, TCP_NO_DELAY, 1 as c_int)
76/// ```
77///
78/// Note that the `val` parameter must be specified correctly; if an option
79/// expects an integer, it is advisable to pass in a `c_int`, not the default
80/// of `i32`.
81#[inline]
82pub fn set_socket_option<T>(fd: c_int, level: c_int, name: c_int, val: &T) -> io::Result<()> {
83    let ret = unsafe {
84        setsockopt(
85            fd,
86            level,
87            name,
88            val as *const _ as *const c_void,
89            mem::size_of::<T>() as socklen_t,
90        )
91    };
92
93    if ret != 0 {
94        return Err(io::Error::last_os_error());
95    }
96
97    Ok(())
98}
99
100/// Sets a collection of multiple socke options with one call.
101pub fn set_socket_option_mult<T>(
102    fd: c_int,
103    level: c_int,
104    name: c_int,
105    values: &[T],
106) -> io::Result<()> {
107    let ret = if values.is_empty() {
108        // can't pass in a ptr to a 0-len slice, pass a null ptr instead
109        unsafe { setsockopt(fd, level, name, ptr::null(), 0) }
110    } else {
111        unsafe {
112            setsockopt(
113                fd,
114                level,
115                name,
116                values.as_ptr().cast(),
117                mem::size_of_val(values) as socklen_t,
118            )
119        }
120    };
121
122    if ret != 0 {
123        return Err(io::Error::last_os_error());
124    }
125
126    Ok(())
127}
128
129// ===== can_frame =====
130
131/// Creates a default C `can_frame`.
132/// This initializes the entire structure to zeros.
133#[inline(always)]
134pub fn can_frame_default() -> can_frame {
135    unsafe { mem::zeroed() }
136}
137
138/// Creates a default C `can_frame`.
139/// This initializes the entire structure to zeros.
140#[inline(always)]
141pub fn canfd_frame_default() -> canfd_frame {
142    unsafe { mem::zeroed() }
143}
144
145/// Check an error return value for timeouts.
146///
147/// Due to the fact that timeouts are reported as errors, calling `read_frame`
148/// on a socket with a timeout that does not receive a frame in time will
149/// result in an error being returned. This trait adds a `should_retry` method
150/// to `Error` and `Result` to check for this condition.
151pub trait ShouldRetry {
152    /// Check for timeout
153    ///
154    /// If `true`, the error is probably due to a timeout.
155    fn should_retry(&self) -> bool;
156}
157
158impl ShouldRetry for io::Error {
159    fn should_retry(&self) -> bool {
160        match self.kind() {
161            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
162            // returned when a timeout occurs. the stdlib already maps EAGAIN
163            // and EWOULDBLOCK os WouldBlock
164            io::ErrorKind::WouldBlock => true,
165            // however, EINPROGRESS is also valid
166            io::ErrorKind::Other => {
167                matches!(self.raw_os_error(), Some(errno) if errno == EINPROGRESS)
168            }
169            _ => false,
170        }
171    }
172}
173
174impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
175    fn should_retry(&self) -> bool {
176        if let Err(ref e) = *self {
177            e.should_retry()
178        } else {
179            false
180        }
181    }
182}
183
184/// CAN socket address.
185///
186/// This is the address for use with CAN sockets. It is simply an addres to
187/// the SocketCAN host interface. It can be created by looking up the name
188/// of the interface, like "can0", "vcan0", etc, or an interface index can
189/// be specified directly, if known. An index of zero can be used to read
190/// frames from all interfaces.
191///
192/// This is based on, and compatible with, the `sockaddr_can` struct from
193/// libc.
194/// [ref](https://docs.rs/libc/latest/libc/struct.sockaddr_can.html)
195#[derive(Clone, Copy)]
196pub struct CanAddr(sockaddr_can);
197
198impl CanAddr {
199    /// Creates a new CAN socket address for the specified interface by index.
200    /// An index of zero can be used to read from all interfaces.
201    pub fn new(ifindex: u32) -> Self {
202        let mut addr = Self::default();
203        addr.0.can_ifindex = ifindex as c_int;
204        addr
205    }
206
207    /// Try to create an address from an interface name.
208    pub fn from_iface(ifname: &str) -> io::Result<Self> {
209        let ifname = CString::new(ifname)?;
210        let ifindex = unsafe { if_nametoindex(ifname.as_ptr()) };
211        if ifindex == 0 {
212            Err(io::Error::last_os_error())
213        } else {
214            Ok(Self::new(ifindex))
215        }
216    }
217
218    /// Gets the address of the structure as a `sockaddr_can` pointer.
219    pub fn as_ptr(&self) -> *const sockaddr_can {
220        &self.0
221    }
222
223    /// Gets the address of the structure as a `sockaddr` pointer.
224    pub fn as_sockaddr_ptr(&self) -> *const sockaddr {
225        self.as_ptr().cast()
226    }
227
228    /// Gets the size of the address structure.
229    pub fn len() -> usize {
230        mem::size_of::<sockaddr_can>()
231    }
232
233    /// Converts the CAN address into a `sockaddr_storage` type.
234    /// This is a generic socket address container with enough space to hold
235    /// any address type in the system.
236    pub fn into_storage(self) -> (sockaddr_storage, socklen_t) {
237        let mut storage: sockaddr_storage = unsafe { mem::zeroed() };
238        unsafe {
239            ptr::copy_nonoverlapping(&self.0, &mut storage as *mut _ as *mut sockaddr_can, 1);
240        }
241        (storage, Self::len() as socklen_t)
242    }
243}
244
245impl Default for CanAddr {
246    fn default() -> Self {
247        let mut addr: sockaddr_can = unsafe { mem::zeroed() };
248        addr.can_family = AF_CAN as sa_family_t;
249        Self(addr)
250    }
251}
252
253impl fmt::Debug for CanAddr {
254    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255        write!(
256            f,
257            "CanAddr {{ can_family: {}, can_ifindex: {} }}",
258            self.0.can_family, self.0.can_ifindex
259        )
260    }
261}
262
263impl From<sockaddr_can> for CanAddr {
264    fn from(addr: sockaddr_can) -> Self {
265        Self(addr)
266    }
267}
268
269impl AsRef<sockaddr_can> for CanAddr {
270    fn as_ref(&self) -> &sockaddr_can {
271        &self.0
272    }
273}
274
275pub fn c_timeval_new(t: Duration) -> timeval {
276    timeval {
277        tv_sec: t.as_secs() as time_t,
278        tv_usec: t.subsec_micros() as suseconds_t,
279    }
280}