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