nell 0.0.2

Linux netlink API access
Documentation
// Copyright (C) 2018 - Will Glozer. All rights reserved.

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)),
        }
    }
}