socketcan_hal/
nl.rs

1//! Netlink module
2//!
3//! The netlink module contains the netlink-based management capabilities of
4//! the socketcan crate. Quoth wikipedia:
5//!
6//!
7//! > Netlink socket family is a Linux kernel interface used for inter-process
8//! > communication (IPC) between both the kernel and userspace processes, and
9//! > between different userspace processes, in a way similar to the Unix
10//! > domain sockets.
11//!
12
13use neli::{
14    consts::{
15        nl::{NlmF, NlmFFlags, NlType},
16        rtnl::{Arphrd, RtAddrFamily, Rtm},
17        socket::NlFamily,
18    },
19    err::NlError,
20    nl::{Nlmsghdr, NlPayload},
21    rtnl::Ifinfomsg,
22    ToBytes,
23    types::RtBuffer,
24    socket::NlSocketHandle,
25};
26use nix::{self, unistd, net::if_::if_nametoindex};
27use std::{
28    os::raw::{c_int, c_uint},
29    fmt::Debug,
30};
31
32/// A result for Netlink errors.
33type NlResult<T> = Result<T, NlError>;
34
35/// SocketCAN interface
36///
37/// Controlled through the kernel's Netlink interface, CAN devices can be
38/// brought up or down or configured through this.
39pub struct CanInterface {
40    if_index: c_uint,
41}
42
43impl CanInterface {
44    /// Open CAN interface by name
45    ///
46    /// Similar to `open_if`, but looks up the device by name instead
47    pub fn open(ifname: &str) -> Result<Self, nix::Error> {
48        let if_index = if_nametoindex(ifname)?;
49        Ok(Self::open_iface(if_index))
50    }
51
52    /// Open CAN interface
53    ///
54    /// Creates a new `CanInterface` instance. No actual "opening" is necessary
55    /// or performed when calling this function.
56    pub fn open_iface(if_index: u32) -> Self {
57        Self { if_index: if_index as c_uint }
58    }
59
60    /// Sends an info message
61    fn send_info_msg(info: Ifinfomsg) -> NlResult<()> {
62        let mut nl = Self::open_route_socket()?;
63
64        // prepare message
65        let hdr = Nlmsghdr::new(
66            None,
67            Rtm::Newlink,
68            NlmFFlags::new(&[NlmF::Request, NlmF::Ack]),
69            None,
70            None,
71            NlPayload::Payload(info),
72        );
73        // send the message
74        Self::send_and_read_ack(&mut nl, hdr)
75    }
76
77
78    /// Sends a netlink message down a netlink socket, and checks if an ACK was
79    /// properly received.
80    fn send_and_read_ack<T, P>(sock: &mut NlSocketHandle, msg: Nlmsghdr<T, P>) -> NlResult<()>
81    where
82        T: NlType + Debug,
83        P: ToBytes + Debug,
84    {
85        sock.send(msg)?;
86        // TODO: Implement this
87        //sock.recv_ack()?;
88        Ok(())
89    }
90
91    /// Opens a new netlink socket, bound to this process' PID
92    fn open_route_socket() -> NlResult<NlSocketHandle> {
93        // retrieve PID
94        let pid = unistd::getpid().as_raw() as u32;
95
96        // open and bind socket
97        // groups is set to None(0), because we want no notifications
98        let sock = NlSocketHandle::connect(NlFamily::Route, Some(pid), &[])?;
99        Ok(sock)
100    }
101
102    /// Bring down CAN interface
103    ///
104    /// Use a netlink control socket to set the interface status to "down".
105    pub fn bring_down(&self) -> NlResult<()> {
106        let info = Ifinfomsg::down(
107            RtAddrFamily::Unspecified,
108            Arphrd::Netrom,
109            self.if_index as c_int,
110            RtBuffer::new()
111        );
112        Self::send_info_msg(info)
113    }
114
115    /// Bring up CAN interface
116    ///
117    /// Brings the interface up by settings its "up" flag enabled via netlink.
118    pub fn bring_up(&self) -> NlResult<()> {
119        let info = Ifinfomsg::up(
120            RtAddrFamily::Unspecified,
121            Arphrd::Netrom,
122            self.if_index as c_int,
123            RtBuffer::new()
124        );
125        Self::send_info_msg(info)
126    }
127}