use std::ffi::CStr;
use std::mem::{self, size_of};
use std::os::raw::c_int;
use std::slice;
use errno::{Errno, errno};
use libc::{self, c_void, AF_NETLINK, SOCK_DGRAM};
use libc::{iovec, msghdr, sendmsg, sockaddr_nl};
use ffi::*;
use self::Error::*;
#[derive(Debug)]
pub enum Error {
Ack,
Syscall(Errno),
Netlink(Errno),
}
pub struct Socket {
fd: c_int,
}
impl Socket {
pub fn new(family: netlink_family) -> Result<Self, Error> {
unsafe {
match libc::socket(AF_NETLINK, SOCK_DGRAM, family as c_int) {
-1 => Err(errno())?,
fd => Ok(Socket{fd}),
}
}
}
pub fn send(&self, iov: &mut [iovec]) -> Result<usize, Error> {
unsafe {
let mut addr: sockaddr_nl = mem::zeroed();
addr.nl_family = AF_NETLINK as u16;
let mut msghdr: msghdr = mem::zeroed();
msghdr.msg_name = &mut addr as *mut _ as *mut c_void;
msghdr.msg_namelen = size_of::<sockaddr_nl>() as u32;
msghdr.msg_iov = iov.as_mut_ptr();
msghdr.msg_iovlen = iov.len() as iovlen;
match sendmsg(self.fd, &msghdr as *const _, 0) {
-1 => Err(errno())?,
n => Ok(n as usize),
}
}
}
pub fn recv<T, U>(&self, f: fn(&T, Attrs) -> Result<U, Error>) -> Result<Vec<U>, Error> {
let mut buf = [0u8; 8192];
let mut vec = Vec::new();
loop {
let mut msgs = recv(self.fd, &mut buf)?;
while let Some((head, data, tail)) = msgs.next() {
match head.nlmsg_type {
NLMSG_DONE => return Ok(vec),
NLMSG_ERROR => error(data)?,
NLMSG_NOOP => continue,
_ => vec.push(f(data, tail)?),
}
if head.nlmsg_flags & NLM_F_MULTI == 0 {
return Ok(vec)
}
}
}
}
}
fn recv(fd: c_int, buf: &mut [u8]) -> Result<Messages, Error> {
unsafe {
let ptr = buf.as_mut_ptr();
let len = buf.len();
match libc::recv(fd, ptr as *mut c_void, len, 0) {
-1 => Err(errno().into()),
n => Ok((ptr, n).into()),
}
}
}
pub fn as_iovec<T>(v: &mut T) -> iovec {
iovec {
iov_base: v as *mut _ as *mut c_void,
iov_len: size_of::<T>(),
}
}
fn align(len: usize) -> usize {
const ALIGNTO: usize = 4;
(len + ALIGNTO - 1) & !(ALIGNTO - 1)
}
fn error<T>(ptr: &T) -> Result<(), &nlmsgerr> {
unsafe {
Err(&*(ptr as *const _ as *const nlmsgerr))
}
}
pub struct Messages {
ptr: *const nlmsghdr,
end: *const nlmsghdr,
}
impl Messages {
pub fn next<T>(&mut self) -> Option<(&nlmsghdr, &T, Attrs)> {
unsafe {
if self.ptr < self.end {
let len = (*self.ptr).nlmsg_len;
let head = self.ptr as *const u8;
let data = head.add(align(size_of::<nlmsghdr>()));
let tail = data.add(align(size_of::<T>()));
let next = head.add(align(len as usize));
let head = &*(head as *const _);
let data = &*(data as *const _);
let tail = (tail, next).into();
self.ptr = next as *const _;
Some((head, data, tail))
} else {
None
}
}
}
}
impl From<(*mut u8, isize)> for Messages {
fn from((ptr, len): (*mut u8, isize)) -> Self {
unsafe {
Messages {
ptr: ptr as *const nlmsghdr,
end: ptr.offset(len) as *const nlmsghdr,
}
}
}
}
pub struct Attrs {
ptr: *const u8,
end: *const u8,
}
#[derive(Debug)]
pub struct Data {
ptr: *const u8,
len: usize,
}
impl Attrs {
pub fn next<T: Attr>(&mut self) -> Option<(&T, Data)> {
unsafe {
if self.ptr < self.end {
let len = Attr::len(&*(self.ptr as *const T));
let head = self.ptr;
let data = head.add(align(size_of::<T>()));
let next = head.add(align(len as usize));
let head = &*(head as *const _);
let data = (data, len - align(size_of::<T>())).into();
self.ptr = next;
Some((head, data))
} else {
None
}
}
}
}
impl From<(*const u8, *const u8)> for Attrs {
fn from((ptr, end): (*const u8, *const u8)) -> Self {
Attrs { ptr, end }
}
}
impl Data {
pub fn attrs(&self) -> Attrs {
unsafe {
let ptr = self.ptr;
let end = self.ptr.add(self.len);
Attrs { ptr, end }
}
}
pub fn cast<T: Default>(&self) -> &T {
unsafe {
assert!(size_of::<T>() <= self.len);
&*(self.ptr as *const T)
}
}
pub fn slice(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self.ptr, self.len)
}
}
pub fn string(&self) -> String {
unsafe {
CStr::from_ptr(self.ptr as *const i8).to_string_lossy().to_string()
}
}
}
impl From<(*const u8, usize)> for Data {
fn from((ptr, len): (*const u8, usize)) -> Self {
Data { ptr, len }
}
}
pub trait Attr {
fn len(&self) -> usize;
}
impl Attr for nlattr {
fn len(&self) -> usize {
self.nla_len as usize
}
}
impl Attr for rtattr {
fn len(&self) -> usize {
self.rta_len as usize
}
}
impl Drop for Socket {
fn drop(&mut self) {
unsafe {
libc::close(self.fd);
}
}
}
impl From<Errno> for Error {
fn from(err: Errno) -> Self {
Syscall(err)
}
}
impl<'a> From<&'a nlmsgerr> for Error {
fn from(err: &nlmsgerr) -> Self {
match err.error {
0 => Ack,
n => Netlink(Errno(-n)),
}
}
}