socketcan_rs/
socket.rs

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