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}