Thanks to visit codestin.com
Credit goes to docs.rs

socketcan/nl/
mod.rs

1// socketcan/src/nl/mod.rs
2//
3// Netlink access to the SocketCAN interfaces.
4//
5// This file is part of the Rust 'socketcan-rs' library.
6//
7// Licensed under the MIT license:
8//   <LICENSE or http://opensource.org/licenses/MIT>
9// This file may not be copied, modified, or distributed except according
10// to those terms.
11
12//! CAN Netlink access
13//!
14//! The netlink module contains the netlink-based management capabilities of
15//! the socketcan crate.
16//!
17//! For SocketCAN, netlink is the primary way for a user-space application to
18//! query or set the parameters of a CAN interface, such as the bitrate, the
19//! control mode bits, and so forth. It also allows the application to get
20//! statistics from the interface and send commands to it, including
21//! performing a bus restart.
22//!
23//1 Netlink is a socket-based mechanism, similar to Unix-domain sockets, which
24//! allows a user-space program communicate with the kernel.
25//!
26//! Unfortunately, the SocketCAN netlink API does not appear to be documented
27//! _anywhere_. The netlink functional summary on the SocketCAN page is here:
28//!
29//! <https://www.kernel.org/doc/html/latest/networking/can.html#netlink-interface-to-set-get-devices-properties>
30//!
31//! The CAN netlink header file for the Linux kernel has the definition of
32//! the constants and data structures that are sent back and forth to the
33//! kernel over netlink. It can be found in the Linux sources here:
34//!
35//! <https://github.com/torvalds/linux/blob/master/include/uapi/linux/can/netlink.h?ts=4>
36//!
37//! The corresponding kernel code that receives and processes messages from
38//! userspace is useful to help figure out what the kernel expects. It's here:
39//!
40//! <https://github.com/torvalds/linux/blob/master/drivers/net/can/dev/netlink.c?ts=4>
41//! <https://github.com/torvalds/linux/blob/master/drivers/net/can/dev/dev.c?ts=4>
42//!
43//! The main Linux user-space client to communicate with network interfaces,
44//! including CAN is _iproute2_. The CAN-specific code for it is here:
45//!
46//! <https://github.com/iproute2/iproute2/blob/main/ip/iplink_can.c?ts=4>
47//!
48//! There is also a C user-space library for SocketCAN, which primarily
49//! deals with the Netlink interface. There are several forks, but one of
50//! the later ones with updated documents is here:
51//!
52//! <https://github.com/lalten/libsocketcan>
53//!
54
55use neli::{
56    attr::Attribute,
57    consts::{
58        nl::{NlType, NlmF, NlmFFlags},
59        rtnl::{Arphrd, RtAddrFamily, Rtm},
60        rtnl::{Iff, IffFlags, Ifla, IflaInfo},
61        socket::NlFamily,
62    },
63    err::NlError,
64    nl::{NlPayload, Nlmsghdr},
65    rtnl::{Ifinfomsg, Rtattr},
66    socket::NlSocketHandle,
67    types::{Buffer, RtBuffer},
68    FromBytes, ToBytes,
69};
70use nix::{self, net::if_::if_nametoindex, unistd};
71use rt::IflaCan;
72use std::{
73    ffi::CStr,
74    fmt::Debug,
75    os::raw::{c_int, c_uint},
76};
77
78/// Low-level Netlink CAN struct bindings.
79mod rt;
80
81use rt::can_ctrlmode;
82pub use rt::CanState;
83
84/// A result for Netlink errors.
85type NlResult<T> = Result<T, NlError>;
86
87/// A Netlink error from an info query
88type NlInfoError = NlError<Rtm, Ifinfomsg>;
89
90/// CAN bit-timing parameters
91pub type CanBitTiming = rt::can_bittiming;
92/// CAN bit-timing const parameters
93pub type CanBitTimingConst = rt::can_bittiming_const;
94/// CAN clock parameter
95pub type CanClock = rt::can_clock;
96/// CAN bus error counters
97pub type CanBerrCounter = rt::can_berr_counter;
98
99/// The details of the interface which can be obtained with the
100/// `CanInterface::details()` function.
101#[allow(missing_copy_implementations)]
102#[derive(Debug, Default, Clone)]
103pub struct InterfaceDetails {
104    /// The name of the interface
105    pub name: Option<String>,
106    /// The index of the interface
107    pub index: c_uint,
108    /// Whether the interface is currently up
109    pub is_up: bool,
110    /// The MTU size of the interface (Standard or FD frames support)
111    pub mtu: Option<Mtu>,
112    /// The CAN-specific parameters for the interface
113    pub can: InterfaceCanParams,
114}
115
116impl InterfaceDetails {
117    /// Creates a new set of interface details with the specified `index`.
118    pub fn new(index: c_uint) -> Self {
119        Self {
120            index,
121            ..Self::default()
122        }
123    }
124}
125
126/// The MTU size for the interface
127///
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129#[repr(u32)]
130pub enum Mtu {
131    /// Standard CAN frame, 8-byte data (16-byte total)
132    Standard = 16,
133    /// FD CAN frame, 64-byte data (64-byte total)
134    Fd = 72,
135}
136
137impl TryFrom<u32> for Mtu {
138    type Error = std::io::Error;
139
140    fn try_from(val: u32) -> Result<Self, Self::Error> {
141        match val {
142            16 => Ok(Mtu::Standard),
143            72 => Ok(Mtu::Fd),
144            _ => Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
145        }
146    }
147}
148
149/// The CAN-specific parameters for the interface.
150#[allow(missing_copy_implementations)]
151#[derive(Debug, Default, Clone)]
152pub struct InterfaceCanParams {
153    /// The CAN bit timing parameters
154    pub bit_timing: Option<CanBitTiming>,
155    /// The bit timing const parameters
156    pub bit_timing_const: Option<CanBitTimingConst>,
157    /// The CAN clock parameters (read only)
158    pub clock: Option<CanClock>,
159    /// The CAN bus state (read-only)
160    pub state: Option<CanState>,
161    /// The automatic restart time (in millisec)
162    /// Zero means auto-restart is disabled.
163    pub restart_ms: Option<u32>,
164    /// The bit error counter (read-only)
165    pub berr_counter: Option<CanBerrCounter>,
166    /// The control mode bits
167    pub ctrl_mode: Option<CanCtrlModes>,
168    /// The FD data bit timing
169    pub data_bit_timing: Option<CanBitTiming>,
170    /// The FD data bit timing const parameters
171    pub data_bit_timing_const: Option<CanBitTimingConst>,
172    /// The CANbus termination resistance
173    pub termination: Option<u16>,
174}
175
176impl TryFrom<&Rtattr<Ifla, Buffer>> for InterfaceCanParams {
177    type Error = NlInfoError;
178
179    /// Try to parse the CAN parameters out of a Linkinfo attribute
180    fn try_from(link_info: &Rtattr<Ifla, Buffer>) -> Result<Self, Self::Error> {
181        let mut params = Self::default();
182
183        for info in link_info.get_attr_handle::<IflaInfo>()?.get_attrs() {
184            if info.rta_type == IflaInfo::Data {
185                for attr in info.get_attr_handle::<IflaCan>()?.get_attrs() {
186                    match attr.rta_type {
187                        IflaCan::BitTiming => {
188                            params.bit_timing = Some(attr.get_payload_as::<CanBitTiming>()?);
189                        }
190                        IflaCan::BitTimingConst => {
191                            params.bit_timing_const =
192                                Some(attr.get_payload_as::<CanBitTimingConst>()?);
193                        }
194                        IflaCan::Clock => {
195                            params.clock = Some(attr.get_payload_as::<CanClock>()?);
196                        }
197                        IflaCan::State => {
198                            params.state = CanState::try_from(attr.get_payload_as::<u32>()?).ok();
199                        }
200                        IflaCan::CtrlMode => {
201                            let ctrl_mode = attr.get_payload_as::<can_ctrlmode>()?;
202                            params.ctrl_mode = Some(CanCtrlModes(ctrl_mode));
203                        }
204                        IflaCan::RestartMs => {
205                            params.restart_ms = Some(attr.get_payload_as::<u32>()?);
206                        }
207                        IflaCan::BerrCounter => {
208                            params.berr_counter = Some(attr.get_payload_as::<CanBerrCounter>()?);
209                        }
210                        IflaCan::DataBitTiming => {
211                            params.data_bit_timing = Some(attr.get_payload_as::<CanBitTiming>()?);
212                        }
213                        IflaCan::DataBitTimingConst => {
214                            params.data_bit_timing_const =
215                                Some(attr.get_payload_as::<CanBitTimingConst>()?);
216                        }
217                        IflaCan::Termination => {
218                            params.termination = Some(attr.get_payload_as::<u16>()?);
219                        }
220                        _ => (),
221                    }
222                }
223            }
224        }
225        Ok(params)
226    }
227}
228
229impl TryFrom<&InterfaceCanParams> for RtBuffer<Ifla, Buffer> {
230    type Error = NlError;
231
232    /// Try to parse the CAN parameters into a NetLink buffer
233    fn try_from(params: &InterfaceCanParams) -> Result<Self, Self::Error> {
234        let mut rtattrs: RtBuffer<Ifla, Buffer> = RtBuffer::new();
235        let mut data = Rtattr::new(None, IflaInfo::Data, Buffer::new())?;
236
237        // TODO: Set the rest of the writable params
238        if let Some(bt) = params.bit_timing {
239            data.add_nested_attribute(&Rtattr::new(None, IflaCan::BitTiming, bt)?)?;
240        }
241        if let Some(r) = params.restart_ms {
242            data.add_nested_attribute(&Rtattr::new(
243                None,
244                IflaCan::RestartMs,
245                &r.to_ne_bytes()[..],
246            )?)?;
247        }
248        if let Some(cm) = params.ctrl_mode {
249            data.add_nested_attribute(&Rtattr::new::<can_ctrlmode>(
250                None,
251                IflaCan::CtrlMode,
252                cm.into(),
253            )?)?;
254        }
255        if let Some(dbt) = params.data_bit_timing {
256            data.add_nested_attribute(&Rtattr::new(None, IflaCan::DataBitTiming, dbt)?)?;
257        }
258        if let Some(t) = params.termination {
259            data.add_nested_attribute(&Rtattr::new(None, IflaCan::Termination, t)?)?;
260        }
261
262        let mut link_info = Rtattr::new(None, Ifla::Linkinfo, Buffer::new())?;
263        link_info.add_nested_attribute(&Rtattr::new(None, IflaInfo::Kind, "can")?)?;
264        link_info.add_nested_attribute(&data)?;
265
266        rtattrs.push(link_info);
267        Ok(rtattrs)
268    }
269}
270
271// ===== CanCtrlMode(s) =====
272
273///
274/// CAN control modes
275///
276/// Note that these correspond to the bit _numbers_ for the control mode bits.
277#[repr(u32)]
278#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
279pub enum CanCtrlMode {
280    /// Loopback mode
281    Loopback,
282    /// Listen-only mode
283    ListenOnly,
284    /// Triple sampling mode
285    TripleSampling,
286    /// One-Shot mode
287    OneShot,
288    /// Bus-error reporting
289    BerrReporting,
290    /// CAN FD mode
291    Fd,
292    /// Ignore missing CAN ACKs
293    PresumeAck,
294    /// CAN FD in non-ISO mode
295    NonIso,
296    /// Classic CAN DLC option
297    CcLen8Dlc,
298}
299
300impl CanCtrlMode {
301    /// Get the mask for the specific control mode
302    pub fn mask(&self) -> u32 {
303        1u32 << (*self as u32)
304    }
305}
306
307/// The collection of control modes
308#[derive(Debug, Default, Clone, Copy)]
309pub struct CanCtrlModes(can_ctrlmode);
310
311impl CanCtrlModes {
312    /// Create a set of CAN control modes from a mask and set of flags.
313    pub fn new(mask: u32, flags: u32) -> Self {
314        Self(can_ctrlmode { mask, flags })
315    }
316
317    /// Create the set of mode flags for a single mode
318    pub fn from_mode(mode: CanCtrlMode, on: bool) -> Self {
319        let mask = mode.mask();
320        let flags = if on { mask } else { 0 };
321        Self::new(mask, flags)
322    }
323
324    /// Adds a mode flag to the existing set of modes.
325    pub fn add(&mut self, mode: CanCtrlMode, on: bool) {
326        let mask = mode.mask();
327        self.0.mask |= mask;
328        if on {
329            self.0.flags |= mask;
330        }
331    }
332
333    /// Clears all of the mode flags in the collection
334    #[inline]
335    pub fn clear(&mut self) {
336        self.0 = can_ctrlmode::default();
337    }
338
339    /// Test if this CanCtrlModes has a specific `mode` turned on
340    ///
341    /// This can be useful for inspecting an [InterfaceCanParams] obtained from
342    /// [CanInterface::details].
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// use socketcan::nl::CanCtrlModes;
348    /// use socketcan::CanCtrlMode;
349    ///
350    /// let modes = CanCtrlModes::new(0x20, 0x20); // This is bit 5 (CanCtrlMode::Fd)
351    /// assert_eq!(modes.has_mode(CanCtrlMode::Fd), true);
352    /// assert_eq!(modes.has_mode(CanCtrlMode::ListenOnly), false);
353    /// ```
354    #[inline]
355    pub fn has_mode(&self, mode: CanCtrlMode) -> bool {
356        (mode.mask() & self.0.flags) != 0
357    }
358}
359
360impl From<can_ctrlmode> for CanCtrlModes {
361    fn from(mode: can_ctrlmode) -> Self {
362        Self(mode)
363    }
364}
365
366impl From<CanCtrlModes> for can_ctrlmode {
367    fn from(mode: CanCtrlModes) -> Self {
368        mode.0
369    }
370}
371
372// ===== CanInterface =====
373
374/// SocketCAN Netlink CanInterface
375///
376/// Controlled through the kernel's Netlink interface, CAN devices can be
377/// brought up or down or configured or queried through this.
378///
379/// Note while that this API is designed in an RAII-fashion, it cannot really
380/// make the same guarantees: It is entirely possible for another user/process
381/// to modify, remove and re-add an interface while you are holding this object
382/// with a reference to it.
383///
384/// Some actions possible on this interface require the process/user to have
385/// the `CAP_NET_ADMIN` capability, like the root user does. This is
386/// indicated by their documentation starting with "PRIVILEGED:".
387#[allow(missing_copy_implementations)]
388#[derive(Debug)]
389pub struct CanInterface {
390    if_index: c_uint,
391}
392
393impl CanInterface {
394    /// Open a CAN interface by name.
395    ///
396    /// Similar to `open_iface`, but looks up the device by name instead of
397    /// the interface index.
398    pub fn open(ifname: &str) -> Result<Self, nix::Error> {
399        let if_index = if_nametoindex(ifname)?;
400        Ok(Self::open_iface(if_index))
401    }
402
403    /// Open a CAN interface.
404    ///
405    /// Creates a new `CanInterface` instance.
406    ///
407    /// Note that no actual "opening" or checks are performed when calling
408    /// this function, nor does it test to determine if the interface with
409    /// the specified index actually exists.
410    pub fn open_iface(if_index: u32) -> Self {
411        let if_index = if_index as c_uint;
412        Self { if_index }
413    }
414
415    /// Creates an `Ifinfomsg` for this CAN interface from a buffer
416    fn info_msg(&self, buf: RtBuffer<Ifla, Buffer>) -> Ifinfomsg {
417        Ifinfomsg::new(
418            RtAddrFamily::Unspecified,
419            Arphrd::Netrom,
420            self.if_index as c_int,
421            IffFlags::empty(),
422            IffFlags::empty(),
423            buf,
424        )
425    }
426
427    /// Sends an info message to the kernel.
428    fn send_info_msg(msg_type: Rtm, info: Ifinfomsg, additional_flags: &[NlmF]) -> NlResult<()> {
429        let mut nl = Self::open_route_socket()?;
430
431        // prepare message
432        let hdr = Nlmsghdr::new(
433            None,
434            msg_type,
435            {
436                let mut flags = NlmFFlags::new(&[NlmF::Request, NlmF::Ack]);
437                for flag in additional_flags {
438                    flags.set(flag);
439                }
440                flags
441            },
442            None,
443            None,
444            NlPayload::Payload(info),
445        );
446        // send the message
447        Self::send_and_read_ack(&mut nl, hdr)
448    }
449
450    /// Sends a message down a netlink socket, and checks if an ACK was
451    /// properly received.
452    fn send_and_read_ack<T, P>(sock: &mut NlSocketHandle, msg: Nlmsghdr<T, P>) -> NlResult<()>
453    where
454        T: NlType + Debug,
455        P: ToBytes + Debug,
456    {
457        sock.send(msg)?;
458
459        // This will actually produce an Err if the response is a netlink error,
460        // no need to match.
461        if let Some(Nlmsghdr {
462            nl_payload: NlPayload::Ack(_),
463            ..
464        }) = sock.recv()?
465        {
466            Ok(())
467        } else {
468            Err(NlError::NoAck)
469        }
470    }
471
472    /// Opens a new netlink socket, bound to this process' PID.
473    /// The function is generic to allow for usage in contexts where NlError
474    /// has specific, non-default, generic parameters.
475    fn open_route_socket<T, P>() -> Result<NlSocketHandle, NlError<T, P>> {
476        // retrieve PID
477        let pid = unistd::Pid::this().as_raw() as u32;
478
479        // open and bind socket
480        // groups is set to None(0), because we want no notifications
481        let sock = NlSocketHandle::connect(NlFamily::Route, Some(pid), &[])?;
482        Ok(sock)
483    }
484
485    /// Sends a query to the kernel and returns the response info message
486    /// to the caller.
487    fn query_details(&self) -> Result<Option<Nlmsghdr<Rtm, Ifinfomsg>>, NlInfoError> {
488        let mut sock = Self::open_route_socket()?;
489
490        let info = self.info_msg({
491            let mut buffer = RtBuffer::new();
492            buffer.push(Rtattr::new(None, Ifla::ExtMask, rt::EXT_FILTER_VF).unwrap());
493            buffer
494        });
495
496        let hdr = Nlmsghdr::new(
497            None,
498            Rtm::Getlink,
499            NlmFFlags::new(&[NlmF::Request]),
500            None,
501            None,
502            NlPayload::Payload(info),
503        );
504
505        sock.send(hdr)?;
506        sock.recv::<'_, Rtm, Ifinfomsg>()
507    }
508
509    /// Bring down this interface.
510    ///
511    /// Use a netlink control socket to set the interface status to "down".
512    pub fn bring_down(&self) -> NlResult<()> {
513        // Specific iface down info
514        let info = Ifinfomsg::down(
515            RtAddrFamily::Unspecified,
516            Arphrd::Netrom,
517            self.if_index as c_int,
518            RtBuffer::new(),
519        );
520        Self::send_info_msg(Rtm::Newlink, info, &[])
521    }
522
523    /// Bring up this interface
524    ///
525    /// Brings the interface up by settings its "up" flag enabled via netlink.
526    pub fn bring_up(&self) -> NlResult<()> {
527        // Specific iface up info
528        let info = Ifinfomsg::up(
529            RtAddrFamily::Unspecified,
530            Arphrd::Netrom,
531            self.if_index as c_int,
532            RtBuffer::new(),
533        );
534        Self::send_info_msg(Rtm::Newlink, info, &[])
535    }
536
537    /// Create a virtual CAN (VCAN) interface.
538    ///
539    /// Useful for testing applications when a physical CAN interface and
540    /// bus is not available.
541    ///
542    /// Note that the length of the name is capped by ```libc::IFNAMSIZ```.
543    ///
544    /// PRIVILEGED: This requires root privilege.
545    ///
546    pub fn create_vcan(name: &str, index: Option<u32>) -> NlResult<Self> {
547        Self::create(name, index, "vcan")
548    }
549
550    /// Create an interface of the given kind.
551    ///
552    /// Note that the length of the name is capped by ```libc::IFNAMSIZ```.
553    ///
554    /// PRIVILEGED: This requires root privilege.
555    ///
556    pub fn create<I>(name: &str, index: I, kind: &str) -> NlResult<Self>
557    where
558        I: Into<Option<u32>>,
559    {
560        if name.len() > libc::IFNAMSIZ {
561            return Err(NlError::Msg("Interface name too long".into()));
562        }
563        let index = index.into();
564
565        let info = Ifinfomsg::new(
566            RtAddrFamily::Unspecified,
567            Arphrd::Netrom,
568            index.unwrap_or(0) as c_int,
569            IffFlags::empty(),
570            IffFlags::empty(),
571            {
572                let mut buffer = RtBuffer::new();
573                buffer.push(Rtattr::new(None, Ifla::Ifname, name)?);
574                let mut linkinfo = Rtattr::new(None, Ifla::Linkinfo, Vec::<u8>::new())?;
575                linkinfo.add_nested_attribute(&Rtattr::new(None, IflaInfo::Kind, kind)?)?;
576                buffer.push(linkinfo);
577                buffer
578            },
579        );
580        Self::send_info_msg(Rtm::Newlink, info, &[NlmF::Create, NlmF::Excl])?;
581
582        if let Some(if_index) = index {
583            Ok(Self { if_index })
584        } else {
585            // Unfortunately netlink does not return the the if_index assigned to the interface.
586            if let Ok(if_index) = if_nametoindex(name) {
587                Ok(Self { if_index })
588            } else {
589                Err(NlError::Msg(
590                    "Interface must have been deleted between request and this if_nametoindex"
591                        .into(),
592                ))
593            }
594        }
595    }
596
597    /// Delete the interface.
598    ///
599    /// PRIVILEGED: This requires root privilege.
600    ///
601    pub fn delete(self) -> Result<(), (Self, NlError)> {
602        let info = self.info_msg(RtBuffer::new());
603        match Self::send_info_msg(Rtm::Dellink, info, &[]) {
604            Ok(()) => Ok(()),
605            Err(err) => Err((self, err)),
606        }
607    }
608
609    /// Attempt to query detailed information on the interface.
610    pub fn details(&self) -> Result<InterfaceDetails, NlInfoError> {
611        match self.query_details()? {
612            Some(msg_hdr) => {
613                let mut info = InterfaceDetails::new(self.if_index);
614
615                if let Ok(payload) = msg_hdr.get_payload() {
616                    info.is_up = payload.ifi_flags.contains(&Iff::Up);
617
618                    for attr in payload.rtattrs.iter() {
619                        match attr.rta_type {
620                            Ifla::Ifname => {
621                                // Note: Use `CStr::from_bytes_until_nul` when MSRV >= 1.69
622                                info.name = CStr::from_bytes_with_nul(attr.rta_payload.as_ref())
623                                    .map(|s| s.to_string_lossy().into_owned())
624                                    .ok();
625                            }
626                            Ifla::Mtu => {
627                                info.mtu = attr
628                                    .get_payload_as::<u32>()
629                                    .ok()
630                                    .and_then(|mtu| Mtu::try_from(mtu).ok());
631                            }
632                            Ifla::Linkinfo => {
633                                info.can = InterfaceCanParams::try_from(attr)?;
634                            }
635                            _ => (),
636                        }
637                    }
638                }
639
640                Ok(info)
641            }
642            None => Err(NlError::NoAck),
643        }
644    }
645
646    /// Set the MTU of this interface.
647    ///
648    /// PRIVILEGED: This requires root privilege.
649    ///
650    pub fn set_mtu(&self, mtu: Mtu) -> NlResult<()> {
651        let mtu = mtu as u32;
652        let info = self.info_msg({
653            let mut buffer = RtBuffer::new();
654            buffer.push(Rtattr::new(None, Ifla::Mtu, &mtu.to_ne_bytes()[..])?);
655            buffer
656        });
657        Self::send_info_msg(Rtm::Newlink, info, &[])
658    }
659
660    /// Set a CAN-specific parameter.
661    ///
662    /// This send a netlink message down to the kernel to set an attribute
663    /// in the link info, such as bitrate, control modes, etc
664    ///
665    /// PRIVILEGED: This requires root privilege.
666    ///
667    pub fn set_can_param<P>(&self, param_type: IflaCan, param: P) -> NlResult<()>
668    where
669        P: ToBytes + neli::Size,
670    {
671        let info = self.info_msg({
672            let mut data = Rtattr::new(None, IflaInfo::Data, Buffer::new())?;
673            data.add_nested_attribute(&Rtattr::new(None, param_type, param)?)?;
674
675            let mut link_info = Rtattr::new(None, Ifla::Linkinfo, Buffer::new())?;
676            link_info.add_nested_attribute(&Rtattr::new(None, IflaInfo::Kind, "can")?)?;
677            link_info.add_nested_attribute(&data)?;
678
679            let mut rtattrs = RtBuffer::new();
680            rtattrs.push(link_info);
681            rtattrs
682        });
683        Self::send_info_msg(Rtm::Newlink, info, &[])
684    }
685
686    /// Set a CAN-specific set of parameters.
687    ///
688    /// This sends a netlink message down to the kernel to set multiple
689    /// attributes in the link info, such as bitrate, control modes, etc.
690    ///
691    /// If you have many attributes to set this is preferred to calling
692    /// [set_can_params][CanInterface::set_can_param] multiple times, since this only sends a
693    /// single netlink message. Also some CAN drivers might only accept
694    /// a set of attributes, not over multiple messages.
695    ///
696    /// PRIVILEGED: This requires root privilege.
697    ///
698    pub fn set_can_params(&self, params: &InterfaceCanParams) -> NlResult<()> {
699        let info = self.info_msg(
700            //RtBuffer<Ifla, Buffer>::try_from(params)?);
701            RtBuffer::try_from(params)?,
702        );
703        /*
704            let mut rtattrs: RtBuffer<Ifla, Buffer> = RtBuffer::new();
705            let mut data = Rtattr::new(None, IflaInfo::Data, Buffer::new())?;
706
707            if let Some(bt) = params.bit_timing {
708                data.add_nested_attribute(&Rtattr::new(None, IflaCan::BitTiming, bt)?)?;
709            }
710            if let Some(r) = params.restart_ms {
711                data.add_nested_attribute(&Rtattr::new(
712                    None,
713                    IflaCan::RestartMs,
714                    &r.to_ne_bytes()[..],
715                )?)?;
716            }
717            if let Some(cm) = params.ctrl_mode {
718                data.add_nested_attribute(&Rtattr::new::<can_ctrlmode>(
719                    None,
720                    IflaCan::CtrlMode,
721                    cm.into(),
722                )?)?;
723            }
724            if let Some(dbt) = params.data_bit_timing {
725                data.add_nested_attribute(&Rtattr::new(None, IflaCan::DataBitTiming, dbt)?)?;
726            }
727            if let Some(t) = params.termination {
728                data.add_nested_attribute(&Rtattr::new(None, IflaCan::Termination, t)?)?;
729            }
730
731            let mut link_info = Rtattr::new(None, Ifla::Linkinfo, Buffer::new())?;
732            link_info.add_nested_attribute(&Rtattr::new(None, IflaInfo::Kind, "can")?)?;
733            link_info.add_nested_attribute(&data)?;
734
735            rtattrs.push(link_info);
736            rtattrs
737        });
738        */
739        Self::send_info_msg(Rtm::Newlink, info, &[])
740    }
741
742    /// Attempt to query an individual CAN parameter on the interface.
743    pub fn can_param<P>(&self, param: IflaCan) -> Result<Option<P>, NlInfoError>
744    where
745        P: for<'a> FromBytes<'a> + Clone,
746    {
747        if let Some(hdr) = self.query_details()? {
748            if let Ok(payload) = hdr.get_payload() {
749                for top_attr in payload.rtattrs.iter() {
750                    if top_attr.rta_type == Ifla::Linkinfo {
751                        for info in top_attr.get_attr_handle::<IflaInfo>()?.get_attrs() {
752                            if info.rta_type == IflaInfo::Data {
753                                for attr in info.get_attr_handle::<IflaCan>()?.get_attrs() {
754                                    if attr.rta_type == param {
755                                        return Ok(Some(attr.get_payload_as::<P>()?));
756                                    }
757                                }
758                            }
759                        }
760                    }
761                }
762            }
763            Ok(None)
764        } else {
765            Err(NlError::NoAck)
766        }
767    }
768
769    /// Gets the current bit rate for the interface.
770    pub fn bit_rate(&self) -> Result<Option<u32>, NlInfoError> {
771        Ok(self.bit_timing()?.map(|timing| timing.bitrate))
772    }
773
774    /// Set the bitrate and, optionally, sample point of this interface.
775    ///
776    /// The bitrate can *not* be changed if the interface is UP. It is
777    /// specified in Hz (bps) while the sample point is given in tenths
778    /// of a percent/
779    ///
780    /// PRIVILEGED: This requires root privilege.
781    ///
782    pub fn set_bitrate<P>(&self, bitrate: u32, sample_point: P) -> NlResult<()>
783    where
784        P: Into<Option<u32>>,
785    {
786        let sample_point: u32 = sample_point.into().unwrap_or(0);
787
788        debug_assert!(
789            0 < bitrate && bitrate <= 1000000,
790            "Bitrate must be within 1..=1000000, received {}.",
791            bitrate
792        );
793        debug_assert!(
794            sample_point < 1000,
795            "Sample point must be within 0..1000, received {}.",
796            sample_point
797        );
798
799        self.set_bit_timing(CanBitTiming {
800            bitrate,
801            sample_point,
802            ..CanBitTiming::default()
803        })
804    }
805
806    /// Gets the bit timing params for the interface
807    pub fn bit_timing(&self) -> Result<Option<CanBitTiming>, NlInfoError> {
808        self.can_param::<CanBitTiming>(IflaCan::BitTiming)
809    }
810
811    /// Sets the bit timing params for the interface
812    ///
813    /// PRIVILEGED: This requires root privilege.
814    ///
815    pub fn set_bit_timing(&self, timing: CanBitTiming) -> NlResult<()> {
816        self.set_can_param(IflaCan::BitTiming, timing)
817    }
818
819    /// Gets the bit timing const data for the interface
820    pub fn bit_timing_const(&self) -> Result<Option<CanBitTimingConst>, NlInfoError> {
821        self.can_param::<CanBitTimingConst>(IflaCan::BitTimingConst)
822    }
823
824    /// Gets the clock frequency for the interface
825    pub fn clock(&self) -> Result<Option<u32>, NlInfoError> {
826        Ok(self
827            .can_param::<CanClock>(IflaCan::Clock)?
828            .map(|clk| clk.freq))
829    }
830
831    /// Gets the state of the interface
832    pub fn state(&self) -> Result<Option<CanState>, NlInfoError> {
833        Ok(self
834            .can_param::<u32>(IflaCan::State)?
835            .and_then(|st| CanState::try_from(st).ok()))
836    }
837
838    /// Set the full control mode (bit) collection.
839    ///
840    /// PRIVILEGED: This requires root privilege.
841    ///
842    #[deprecated(since = "3.2.0", note = "Use `set_ctrlmodes` instead")]
843    pub fn set_full_ctrlmode(&self, ctrlmode: can_ctrlmode) -> NlResult<()> {
844        self.set_can_param(IflaCan::CtrlMode, ctrlmode)
845    }
846
847    /// Set the full control mode (bit) collection.
848    ///
849    /// PRIVILEGED: This requires root privilege.
850    ///
851    pub fn set_ctrlmodes<M>(&self, ctrlmode: M) -> NlResult<()>
852    where
853        M: Into<CanCtrlModes>,
854    {
855        let modes = ctrlmode.into();
856        let modes: can_ctrlmode = modes.into();
857        self.set_can_param(IflaCan::CtrlMode, modes)
858    }
859
860    /// Set or clear an individual control mode parameter.
861    ///
862    /// PRIVILEGED: This requires root privilege.
863    ///
864    pub fn set_ctrlmode(&self, mode: CanCtrlMode, on: bool) -> NlResult<()> {
865        self.set_ctrlmodes(CanCtrlModes::from_mode(mode, on))
866    }
867
868    /// Gets the automatic CANbus restart time for the interface, in milliseconds.
869    pub fn restart_ms(&self) -> Result<Option<u32>, NlInfoError> {
870        self.can_param::<u32>(IflaCan::RestartMs)
871    }
872
873    /// Set the automatic restart milliseconds of the interface
874    ///
875    /// PRIVILEGED: This requires root privilege.
876    ///
877    pub fn set_restart_ms(&self, restart_ms: u32) -> NlResult<()> {
878        self.set_can_param(IflaCan::RestartMs, &restart_ms.to_ne_bytes()[..])
879    }
880
881    /// Manually restart the interface.
882    ///
883    /// Note that a manual restart if only permitted if automatic restart is
884    /// disabled and the device is in the bus-off state.
885    /// See: linux/drivers/net/can/dev/dev.c
886    ///
887    /// PRIVILEGED: This requires root privilege.
888    ///
889    /// Common Errors:
890    ///     EINVAL - The interface is down or automatic restarts are enabled
891    ///     EBUSY - The interface is not in a bus-off state
892    ///
893    pub fn restart(&self) -> NlResult<()> {
894        // Note: The linux code shows the data type to be u32, but never
895        // appears to access the value sent. iproute2 sends a 1, so we do
896        // too!
897        // See: linux/drivers/net/can/dev/netlink.c
898        let restart_data: u32 = 1;
899        self.set_can_param(IflaCan::Restart, &restart_data.to_ne_bytes()[..])
900    }
901
902    /// Gets the bus error counter from the interface
903    pub fn berr_counter(&self) -> Result<Option<CanBerrCounter>, NlInfoError> {
904        self.can_param::<CanBerrCounter>(IflaCan::BerrCounter)
905    }
906
907    /// Gets the data bit timing params for the interface
908    pub fn data_bit_timing(&self) -> Result<Option<CanBitTiming>, NlInfoError> {
909        self.can_param::<CanBitTiming>(IflaCan::DataBitTiming)
910    }
911
912    /// Sets the data bit timing params for the interface
913    ///
914    /// PRIVILEGED: This requires root privilege.
915    ///
916    pub fn set_data_bit_timing(&self, timing: CanBitTiming) -> NlResult<()> {
917        self.set_can_param(IflaCan::DataBitTiming, timing)
918    }
919
920    /// Set the data bitrate and, optionally, data sample point of this
921    /// interface.
922    ///
923    /// This only applies to interfaces in FD mode.
924    ///
925    /// The data bitrate can *not* be changed if the interface is UP. It is
926    /// specified in Hz (bps) while the sample point is given in tenths
927    /// of a percent/
928    ///
929    /// PRIVILEGED: This requires root privilege.
930    ///
931    pub fn set_data_bitrate<P>(&self, bitrate: u32, sample_point: P) -> NlResult<()>
932    where
933        P: Into<Option<u32>>,
934    {
935        let sample_point: u32 = sample_point.into().unwrap_or(0);
936
937        self.set_data_bit_timing(CanBitTiming {
938            bitrate,
939            sample_point,
940            ..CanBitTiming::default()
941        })
942    }
943
944    /// Gets the data bit timing const params for the interface
945    pub fn data_bit_timing_const(&self) -> Result<Option<CanBitTimingConst>, NlInfoError> {
946        self.can_param::<CanBitTimingConst>(IflaCan::DataBitTimingConst)
947    }
948
949    /// Sets the CANbus termination for the interface
950    ///
951    /// Not all interfaces support setting a termination.
952    /// Termination is in ohms. Your interface most likely only supports
953    /// certain values. Common values are 0 and 120.
954    ///
955    /// PRIVILEGED: This requires root privilege.
956    ///
957    pub fn set_termination(&self, termination: u16) -> NlResult<()> {
958        self.set_can_param(IflaCan::Termination, termination)
959    }
960
961    /// Gets the CANbus termination for the interface
962    pub fn termination(&self) -> Result<Option<u16>, NlInfoError> {
963        self.can_param::<u16>(IflaCan::Termination)
964    }
965}
966
967/////////////////////////////////////////////////////////////////////////////
968
969/// Netlink tests for SocketCAN control
970#[cfg(feature = "netlink_tests")]
971#[cfg(test)]
972pub mod tests {
973    use super::*;
974    use serial_test::serial;
975    use std::ops::Deref;
976
977    /// RAII-style helper to create and clean-up a specific vcan interface for a single test.
978    /// Using drop here ensures that the interface always gets cleaned up
979    /// (although a restart would also remove it).
980    ///
981    /// Intended for use (ONLY) in tests as follows:
982    /// ```
983    /// #[test]
984    /// fn my_test() {
985    ///     let interface = TemporaryInterface::new("my_test").unwrap();
986    ///     // use the interface..
987    /// }
988    /// ```
989    /// Please note that there is a limit to the length of interface names,
990    /// namely 16 characters on Linux.
991    #[allow(missing_copy_implementations)]
992    #[derive(Debug)]
993    pub struct TemporaryInterface {
994        interface: CanInterface,
995    }
996
997    impl TemporaryInterface {
998        /// Creates a temporaty interface
999        #[allow(unused)]
1000        pub fn new(name: &str) -> NlResult<Self> {
1001            Ok(Self {
1002                interface: CanInterface::create_vcan(name, None)?,
1003            })
1004        }
1005    }
1006
1007    impl Drop for TemporaryInterface {
1008        fn drop(&mut self) {
1009            assert!(CanInterface::open_iface(self.interface.if_index)
1010                .delete()
1011                .is_ok());
1012        }
1013    }
1014
1015    impl Deref for TemporaryInterface {
1016        type Target = CanInterface;
1017
1018        fn deref(&self) -> &Self::Target {
1019            &self.interface
1020        }
1021    }
1022
1023    #[test]
1024    #[serial]
1025    fn up_down() {
1026        let interface = TemporaryInterface::new("up_down").unwrap();
1027
1028        assert!(interface.bring_up().is_ok());
1029        assert!(interface.details().unwrap().is_up);
1030
1031        assert!(interface.bring_down().is_ok());
1032        assert!(!interface.details().unwrap().is_up);
1033    }
1034
1035    #[test]
1036    #[serial]
1037    fn details() {
1038        let interface = TemporaryInterface::new("info").unwrap();
1039        let details = interface.details().unwrap();
1040        assert_eq!("info", details.name.unwrap());
1041        assert!(details.mtu.is_some());
1042        assert!(!details.is_up);
1043    }
1044
1045    #[test]
1046    #[serial]
1047    fn mtu() {
1048        let interface = TemporaryInterface::new("mtu").unwrap();
1049
1050        assert!(interface.set_mtu(Mtu::Fd).is_ok());
1051        assert_eq!(Mtu::Fd, interface.details().unwrap().mtu.unwrap());
1052
1053        assert!(interface.set_mtu(Mtu::Standard).is_ok());
1054        assert_eq!(Mtu::Standard, interface.details().unwrap().mtu.unwrap());
1055    }
1056}