tokio_socketcan_isotp/
socketcan_isotp.rs

1#![deny(clippy::all)]
2#![allow(dead_code)]
3//! Code from [socketcan-isotp](https://github.com/marcelbuesing/socketcan-isotp)
4//! and added read_to_vec() method
5//!
6//! Socketcan ISO-TP support.
7//!
8//! The Linux kernel supports using CAN-devices through a
9//! [network-like API](https://www.kernel.org/doc/Documentation/networking/can.txt).
10//! This crate allows easy access to this functionality without having to wrestle
11//! libc calls.
12//!
13//! ISO-TP allows sending data packets that exceed the eight byte of a default CAN frame.
14//! [can-isotp](https://github.com/hartkopp/can-isotp) is an ISO-TP kernel module that takes
15//! care of handling the ISO-TP protocol.
16//!
17//! Instructions on how the can-isotp kernel module can be build and loaded can be found
18//! at [https://github.com/hartkopp/can-isotp](https://github.com/hartkopp/can-isotp) .
19//!
20//! ```rust,no_run
21//! use socketcan_isotp::IsoTpSocket;
22//!
23//! fn main() -> Result<(), socketcan_isotp::Error> {
24//!     let mut tp_socket = IsoTpSocket::open(
25//!         "vcan0",
26//!         0x123,
27//!         0x321
28//!     )?;
29//!
30//!     loop {
31//!         let buffer = tp_socket.read()?;
32//!         println!("read {} bytes", buffer.len());
33//!
34//!         // print TP frame data
35//!         for x in buffer {
36//!             print!("{:X?} ", x);
37//!         }
38//!
39//!         println!("");
40//!     }
41//!
42//!     Ok(())
43//! }
44//! ```
45//!
46
47use bitflags::bitflags;
48pub use embedded_can::{ExtendedId, Id, StandardId};
49use libc::{
50    bind, c_int, c_short, c_void, close, fcntl, read, setsockopt, sockaddr, socket, write, F_GETFL,
51    F_SETFL, O_NONBLOCK, SOCK_DGRAM,
52};
53use nix::net::if_::if_nametoindex;
54use std::convert::TryFrom;
55use std::convert::TryInto;
56use std::io;
57use std::mem::size_of;
58use std::num::TryFromIntError;
59use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
60use std::time::Duration;
61use thiserror::Error;
62
63/// CAN address family
64pub const AF_CAN: c_short = 29;
65
66/// CAN protocol family
67pub const PF_CAN: c_int = 29;
68
69/// ISO 15765-2 Transport Protocol
70pub const CAN_ISOTP: c_int = 6;
71
72/// undocumented can.h constant
73pub const SOL_CAN_BASE: c_int = 100;
74
75/// undocumented isotp.h constant
76pub const SOL_CAN_ISOTP: c_int = SOL_CAN_BASE + CAN_ISOTP;
77
78/// pass struct `IsoTpOptions`
79pub const CAN_ISOTP_OPTS: c_int = 1;
80
81/// pass struct `FlowControlOptions`
82pub const CAN_ISOTP_RECV_FC: c_int = 2;
83
84/// pass __u32 value in nano secs
85/// use this time instead of value
86/// provided in FC from the receiver
87pub const CAN_ISOTP_TX_STMIN: c_int = 3;
88
89/// pass __u32 value in nano secs
90/// ignore received CF frames which
91/// timestamps differ less than val
92pub const CAN_ISOTP_RX_STMIN: c_int = 4;
93
94/// pass struct `LinkLayerOptions`
95pub const CAN_ISOTP_LL_OPTS: c_int = 5;
96
97/// `CAN_MAX_DLEN` According to ISO 11898-1
98pub const CAN_MAX_DLEN: u8 = 8;
99
100/// Size of buffer allocated for reading TP data
101pub const RECV_BUFFER_SIZE: usize = 4096;
102
103/// Size of a canframe, constant to reduce crate dependencies
104/// `std::mem::size_of::<socketcan::CANFrame>())`
105const SIZE_OF_CAN_FRAME: u8 = 16;
106
107const FLOW_CONTROL_OPTIONS_SIZE: usize = size_of::<FlowControlOptions>();
108
109const ISOTP_OPTIONS_SIZE: usize = size_of::<IsoTpOptions>();
110
111const LINK_LAYER_OPTIONS_SIZE: usize = size_of::<LinkLayerOptions>();
112
113const CAN_ISOTP_DEFAULT_RECV_BS: u8 = 0;
114
115const CAN_ISOTP_DEFAULT_RECV_STMIN: u8 = 0x00;
116
117const CAN_ISOTP_DEFAULT_RECV_WFTMAX: u8 = 0;
118
119bitflags! {
120    pub struct IsoTpBehaviour: u32 {
121        /// listen only (do not send FC)
122        const CAN_ISOTP_LISTEN_MODE = 0x001;
123        /// enable extended addressing
124        const CAN_ISOTP_EXTEND_ADDR	= 0x002;
125        /// enable CAN frame padding tx path
126        const CAN_ISOTP_TX_PADDING	= 0x004;
127        /// enable CAN frame padding rx path
128        const CAN_ISOTP_RX_PADDING	= 0x008;
129        /// check received CAN frame padding
130        const CAN_ISOTP_CHK_PAD_LEN	= 0x010;
131        /// check received CAN frame padding
132        const CAN_ISOTP_CHK_PAD_DATA = 0x020;
133        /// half duplex error state handling
134        const CAN_ISOTP_HALF_DUPLEX = 0x040;
135        /// ignore stmin from received FC
136        const CAN_ISOTP_FORCE_TXSTMIN = 0x080;
137        /// ignore CFs depending on rx stmin
138        const CAN_ISOTP_FORCE_RXSTMIN = 0x100;
139        /// different rx extended addressing
140        const CAN_ISOTP_RX_EXT_ADDR = 0x200;
141    }
142}
143
144/// if set, indicate 29 bit extended format
145pub const EFF_FLAG: u32 = 0x8000_0000;
146
147/// remote transmission request flag
148pub const RTR_FLAG: u32 = 0x4000_0000;
149
150/// error flag
151pub const ERR_FLAG: u32 = 0x2000_0000;
152
153/// valid bits in standard frame id
154pub const SFF_MASK: u32 = 0x0000_07ff;
155
156/// valid bits in extended frame id
157pub const EFF_MASK: u32 = 0x1fff_ffff;
158
159/// valid bits in error frame
160pub const ERR_MASK: u32 = 0x1fff_ffff;
161
162/// an error mask that will cause Socketcan to report all errors
163pub const ERR_MASK_ALL: u32 = ERR_MASK;
164
165/// an error mask that will cause Socketcan to silently drop all errors
166pub const ERR_MASK_NONE: u32 = 0;
167
168#[derive(Debug)]
169#[repr(C)]
170struct CanAddr {
171    _af_can: c_short,
172    if_index: c_int, // address familiy,
173    /// transport protocol class address information
174    rx_id: u32,
175    /// transport protocol class address information
176    tx_id: u32,
177    _pgn: u32,
178    _addr: u8,
179}
180
181/// ISO-TP otions aka `can_isotp_options`
182#[repr(C)]
183pub struct IsoTpOptions {
184    /// set flags for isotp behaviour.
185    flags: u32,
186    /// frame transmission time (N_As/N_Ar)
187    /// time in nano secs
188    frame_txtime: u32,
189    /// set address for extended addressing
190    ext_address: u8,
191    /// set content of padding byte (tx)
192    txpad_content: u8,
193    /// set content of padding byte (rx)
194    rxpad_content: u8,
195    /// set address for extended addressing
196    rx_ext_address: u8,
197}
198
199impl IsoTpOptions {
200    pub fn new(
201        flags: IsoTpBehaviour,
202        frame_txtime: Duration,
203        ext_address: u8,
204        txpad_content: u8,
205        rxpad_content: u8,
206        rx_ext_address: u8,
207    ) -> Result<Self, TryFromIntError> {
208        let flags = flags.bits();
209        let frame_txtime = u32::try_from(frame_txtime.as_nanos())?;
210
211        Ok(Self {
212            flags,
213            frame_txtime,
214            ext_address,
215            txpad_content,
216            rxpad_content,
217            rx_ext_address,
218        })
219    }
220
221    /// get flags for isotp behaviour.
222    pub fn get_flags(&self) -> Option<IsoTpBehaviour> {
223        IsoTpBehaviour::from_bits(self.flags)
224    }
225
226    /// set flags for isotp behaviour.
227    pub fn set_flags(&mut self, flags: IsoTpBehaviour) {
228        self.flags = flags.bits();
229    }
230
231    /// get frame transmission time (N_As/N_Ar)
232    pub fn get_frame_txtime(&self) -> Duration {
233        Duration::from_nanos(self.frame_txtime.into())
234    }
235
236    /// set frame transmission time (N_As/N_Ar)
237    pub fn set_frame_txtime(&mut self, frame_txtime: Duration) -> Result<(), TryFromIntError> {
238        self.frame_txtime = u32::try_from(frame_txtime.as_nanos())?;
239        Ok(())
240    }
241
242    /// get frame transmission time (N_As/N_Ar)
243    pub fn get_ext_address(&self) -> u8 {
244        self.ext_address
245    }
246
247    /// set address for extended addressing
248    pub fn set_ext_address(&mut self, ext_address: u8) {
249        self.ext_address = ext_address;
250    }
251
252    /// get address for extended addressing
253    pub fn get_txpad_content(&self) -> u8 {
254        self.txpad_content
255    }
256
257    /// set content of padding byte (tx)
258    pub fn set_txpad_content(&mut self, txpad_content: u8) {
259        self.txpad_content = txpad_content;
260    }
261
262    /// get content of padding byte (rx)
263    pub fn get_rxpad_content(&self) -> u8 {
264        self.rxpad_content
265    }
266
267    /// set content of padding byte (rx)
268    pub fn set_rxpad_content(&mut self, rxpad_content: u8) {
269        self.rxpad_content = rxpad_content;
270    }
271
272    /// get address for extended addressing
273    pub fn get_rx_ext_address(&self) -> u8 {
274        self.rx_ext_address
275    }
276
277    /// set address for extended addressing
278    pub fn set_rx_ext_address(&mut self, rx_ext_address: u8) {
279        self.rx_ext_address = rx_ext_address;
280    }
281}
282
283impl Default for IsoTpOptions {
284    fn default() -> Self {
285        // Defaults defined in linux/can/isotp.h
286        Self {
287            flags: 0x00,
288            frame_txtime: 0x00,
289            ext_address: 0x00,
290            txpad_content: 0xCC,
291            rxpad_content: 0xCC,
292            rx_ext_address: 0x00,
293        }
294    }
295}
296
297/// Flow control options aka `can_isotp_fc_options`
298#[repr(C)]
299pub struct FlowControlOptions {
300    /// blocksize provided in FC frame
301    /// 0 = off
302    bs: u8,
303    /// separation time provided in FC frame
304    ///
305    /// 0x00 - 0x7F : 0 - 127 ms
306    /// 0x80 - 0xF0 : reserved
307    /// 0xF1 - 0xF9 : 100 us - 900 us
308    /// 0xFA - 0xFF : reserved
309    stmin: u8,
310    /// max. number of wait frame transmiss.
311    /// 0 = omit FC N_PDU WT
312    wftmax: u8,
313}
314
315impl Default for FlowControlOptions {
316    fn default() -> Self {
317        Self {
318            bs: CAN_ISOTP_DEFAULT_RECV_BS,
319            stmin: CAN_ISOTP_DEFAULT_RECV_STMIN,
320            wftmax: CAN_ISOTP_DEFAULT_RECV_WFTMAX,
321        }
322    }
323}
324
325impl FlowControlOptions {
326    /// Creates new flow control options
327    /// # Parameters
328    /// * bs - Blocksize for the ISO-TP frame, set 0 for off (Sending ECU shall send all frames in 1 block)
329    /// * stmin - Separation time between consecutive frames. The following values are allowed:
330    ///     * 0x00 - 0x7F : 0 - 127 ms
331    ///     * 0xF1 - 0xF9 : 100 - 900 us
332    /// * wftmax - Maximum number of wait frame transmiss
333    ///     * Default value is 0 (omit)
334    pub fn new(bs: u8, stmin: u8, wftmax: u8) -> Self {
335        Self { bs, stmin, wftmax }
336    }
337}
338
339bitflags! {
340    pub struct TxFlags: u8 {
341        /// bit rate switch (second bitrate for payload data)
342        const CANFD_BRS = 0x01;
343        /// error state indicator of the transmitting node
344        const CANFD_ESI	= 0x02;
345    }
346}
347
348/// Link layer options aka `can_isotp_ll_options`
349#[repr(C)]
350pub struct LinkLayerOptions {
351    /// generated & accepted CAN frame type
352    /// CAN_MTU   (16) -> standard CAN 2.0
353    /// CANFD_MTU (72) -> CAN FD frame
354    mtu: u8,
355    /// tx link layer data length in bytes
356    /// (configured maximum payload length)
357    /// __u8 value : 8,12,16,20,24,32,48,64
358    /// => rx path supports all LL_DL values
359    tx_dl: u8,
360    /// set into struct canfd_frame.flags
361    /// at frame creation: e.g. CANFD_BRS
362    /// Obsolete when the BRS flag is fixed
363    /// by the CAN netdriver configuration
364    tx_flags: u8,
365}
366
367impl LinkLayerOptions {
368    pub fn new(mtu: u8, tx_dl: u8, tx_flags: TxFlags) -> Self {
369        let tx_flags = tx_flags.bits();
370        Self {
371            mtu,
372            tx_dl,
373            tx_flags,
374        }
375    }
376}
377
378impl Default for LinkLayerOptions {
379    fn default() -> Self {
380        Self {
381            // CAN_ISOTP_DEFAULT_LL_MTU
382            mtu: SIZE_OF_CAN_FRAME,
383            // CAN_ISOTP_DEFAULT_LL_TX_DL
384            tx_dl: CAN_MAX_DLEN,
385            // CAN_ISOTP_DEFAULT_LL_TX_FLAGS
386            tx_flags: 0x00,
387        }
388    }
389}
390
391#[derive(Error, Debug)]
392/// Possible errors
393pub enum Error {
394    /// CAN device could not be found
395    #[error("Failed to find can device: {source:?}")]
396    Lookup {
397        #[from]
398        source: nix::Error,
399    },
400
401    /// IO Error
402    #[error("IO error: {source:?}")]
403    Io {
404        #[from]
405        source: io::Error,
406    },
407}
408/// An ISO-TP socketcan socket.
409///
410/// Will be closed upon deallocation. To close manually, use `std::drop::Drop`.
411/// Internally this is just a wrapped file-descriptor.
412pub struct IsoTpSocket {
413    fd: c_int,
414    recv_buffer: [u8; RECV_BUFFER_SIZE],
415}
416
417impl IsoTpSocket {
418    /// Open a named CAN ISO-TP device.
419    ///
420    /// Usually the more common case, opens a socket can device by name, such
421    /// as "vcan0" or "socan0".
422    pub fn open(ifname: &str, src: impl Into<Id>, dst: impl Into<Id>) -> Result<Self, Error> {
423        Self::open_with_opts(
424            ifname,
425            src,
426            dst,
427            Some(IsoTpOptions::default()),
428            Some(FlowControlOptions::default()),
429            Some(LinkLayerOptions::default()),
430        )
431    }
432
433    /// Open a named CAN ISO-TP device, passing additional options.
434    ///
435    /// Usually the more common case, opens a socket can device by name, such
436    /// as "vcan0" or "socan0".
437    pub fn open_with_opts(
438        ifname: &str,
439        src: impl Into<Id>,
440        dst: impl Into<Id>,
441        isotp_options: Option<IsoTpOptions>,
442        rx_flow_control_options: Option<FlowControlOptions>,
443        link_layer_options: Option<LinkLayerOptions>,
444    ) -> Result<Self, Error> {
445        let if_index = if_nametoindex(ifname)?;
446        Self::open_if_with_opts(
447            if_index.try_into().unwrap(),
448            src,
449            dst,
450            isotp_options,
451            rx_flow_control_options,
452            link_layer_options,
453        )
454    }
455
456    /// Open CAN ISO-TP device device by interface number.
457    ///
458    /// Opens a CAN device by kernel interface number.
459    pub fn open_if(if_index: c_int, src: impl Into<Id>, dst: impl Into<Id>) -> Result<Self, Error> {
460        Self::open_if_with_opts(
461            if_index,
462            src,
463            dst,
464            Some(IsoTpOptions::default()),
465            Some(FlowControlOptions::default()),
466            Some(LinkLayerOptions::default()),
467        )
468    }
469
470    /// Open CAN ISO-TP device device by interface number, passing additional options.
471    ///
472    /// Opens a CAN device by kernel interface number.
473    pub fn open_if_with_opts(
474        if_index: c_int,
475        src: impl Into<Id>,
476        dst: impl Into<Id>,
477        isotp_options: Option<IsoTpOptions>,
478        rx_flow_control_options: Option<FlowControlOptions>,
479        link_layer_options: Option<LinkLayerOptions>,
480    ) -> Result<Self, Error> {
481        let rx_id = match src.into() {
482            Id::Standard(standard_id) => standard_id.as_raw() as u32,
483            Id::Extended(extended_id) => extended_id.as_raw() | EFF_FLAG,
484        };
485        let tx_id = match dst.into() {
486            Id::Standard(standard_id) => standard_id.as_raw() as u32,
487            Id::Extended(extended_id) => extended_id.as_raw() | EFF_FLAG,
488        };
489        let addr = CanAddr {
490            _af_can: AF_CAN,
491            if_index,
492            rx_id,
493            tx_id,
494            _pgn: 0,
495            _addr: 0,
496        };
497
498        // open socket
499        let sock_fd;
500        unsafe {
501            sock_fd = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP);
502        }
503
504        if sock_fd == -1 {
505            return Err(Error::from(io::Error::last_os_error()));
506        }
507
508        // Set IsoTpOptions
509        if let Some(isotp_options) = isotp_options {
510            let isotp_options_ptr: *const c_void = &isotp_options as *const _ as *const c_void;
511            let err = unsafe {
512                setsockopt(
513                    sock_fd,
514                    SOL_CAN_ISOTP,
515                    CAN_ISOTP_OPTS,
516                    isotp_options_ptr,
517                    ISOTP_OPTIONS_SIZE.try_into().unwrap(),
518                )
519            };
520            if err == -1 {
521                return Err(Error::from(io::Error::last_os_error()));
522            }
523        }
524
525        // Set FlowControlOptions
526        if let Some(rx_flow_control_options) = rx_flow_control_options {
527            let rx_flow_control_options_ptr: *const c_void =
528                &rx_flow_control_options as *const _ as *const c_void;
529            let err = unsafe {
530                setsockopt(
531                    sock_fd,
532                    SOL_CAN_ISOTP,
533                    CAN_ISOTP_RECV_FC,
534                    rx_flow_control_options_ptr,
535                    FLOW_CONTROL_OPTIONS_SIZE.try_into().unwrap(),
536                )
537            };
538            if err == -1 {
539                return Err(Error::from(io::Error::last_os_error()));
540            }
541        }
542
543        // Set LinkLayerOptions
544        if let Some(link_layer_options) = link_layer_options {
545            let link_layer_options_ptr: *const c_void =
546                &link_layer_options as *const _ as *const c_void;
547            let err = unsafe {
548                setsockopt(
549                    sock_fd,
550                    SOL_CAN_ISOTP,
551                    CAN_ISOTP_LL_OPTS,
552                    link_layer_options_ptr,
553                    LINK_LAYER_OPTIONS_SIZE.try_into().unwrap(),
554                )
555            };
556            if err == -1 {
557                return Err(Error::from(io::Error::last_os_error()));
558            }
559        }
560
561        // bind it
562        let bind_rv;
563        unsafe {
564            let sockaddr_ptr = &addr as *const CanAddr;
565            bind_rv = bind(
566                sock_fd,
567                sockaddr_ptr as *const sockaddr,
568                size_of::<CanAddr>().try_into().unwrap(),
569            );
570        }
571
572        // FIXME: on fail, close socket (do not leak socketfds)
573        if bind_rv == -1 {
574            let e = io::Error::last_os_error();
575            unsafe {
576                close(sock_fd);
577            }
578            return Err(Error::from(e));
579        }
580
581        Ok(Self {
582            fd: sock_fd,
583            recv_buffer: [0x00; RECV_BUFFER_SIZE],
584        })
585    }
586
587    fn close(&mut self) -> io::Result<()> {
588        unsafe {
589            let rv = close(self.fd);
590            if rv != -1 {
591                return Err(io::Error::last_os_error());
592            }
593        }
594        Ok(())
595    }
596
597    /// Change socket to non-blocking mode
598    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
599        // retrieve current flags
600        let oldfl = unsafe { fcntl(self.fd, F_GETFL) };
601
602        if oldfl == -1 {
603            return Err(io::Error::last_os_error());
604        }
605
606        let newfl = if nonblocking {
607            oldfl | O_NONBLOCK
608        } else {
609            oldfl & !O_NONBLOCK
610        };
611
612        let rv = unsafe { fcntl(self.fd, F_SETFL, newfl) };
613
614        if rv != 0 {
615            return Err(io::Error::last_os_error());
616        }
617        Ok(())
618    }
619
620    /// Blocking read data
621    pub fn read(&mut self) -> io::Result<&[u8]> {
622        let buffer_ptr = &mut self.recv_buffer as *mut _ as *mut c_void;
623
624        let read_rv = unsafe { read(self.fd, buffer_ptr, RECV_BUFFER_SIZE) };
625
626        if read_rv < 0 {
627            return Err(io::Error::last_os_error());
628        }
629
630        Ok(&self.recv_buffer[0..read_rv.try_into().unwrap()])
631    }
632
633    pub fn write(&self, buffer: &[u8]) -> io::Result<()> {
634        let write_rv = unsafe {
635            let buffer_ptr = buffer as *const _ as *const c_void;
636            write(self.fd, buffer_ptr, buffer.len())
637        };
638        if write_rv != buffer.len().try_into().unwrap() {
639            return Err(io::Error::last_os_error());
640        }
641        Ok(())
642    }
643
644    pub fn read_to_vec(&self) -> io::Result<Vec<u8>> {
645        let mut buffer: [u8; RECV_BUFFER_SIZE] = [0; RECV_BUFFER_SIZE];
646        let buffer_ptr = &mut buffer as *mut _ as *mut c_void;
647
648        let read_rv = unsafe { read(self.fd, buffer_ptr, RECV_BUFFER_SIZE) };
649
650        if read_rv < 0 {
651            return Err(io::Error::last_os_error());
652        }
653
654        Ok(buffer[0..read_rv.try_into().unwrap()].to_vec())
655    }
656}
657
658impl AsRawFd for IsoTpSocket {
659    fn as_raw_fd(&self) -> RawFd {
660        self.fd
661    }
662}
663
664impl FromRawFd for IsoTpSocket {
665    unsafe fn from_raw_fd(fd: RawFd) -> Self {
666        Self {
667            fd,
668            recv_buffer: [0x00; RECV_BUFFER_SIZE],
669        }
670    }
671}
672
673impl IntoRawFd for IsoTpSocket {
674    fn into_raw_fd(self) -> RawFd {
675        self.fd
676    }
677}
678
679impl Drop for IsoTpSocket {
680    fn drop(&mut self) {
681        self.close().ok(); // ignore result
682    }
683}