nell 0.0.1

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

use std::mem::size_of;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use libc::{in_addr, in6_addr};
use ffi::*;
use ffi::netlink_family::*;
use sys::*;

#[derive(Debug)]
pub struct Diag {
    pub family: u8,
    pub src:    SocketAddr,
    pub dst:    SocketAddr,
    pub state:  u8,
    pub info:   Option<tcp_info>,
}

pub fn diag(req: &mut inet_diag_req_v2) -> Result<Vec<Diag>, Error> {
    let sock = Socket::new(NETLINK_SOCK_DIAG)?;

    let mut hdr = nlmsghdr {
        nlmsg_len:   nlmsg_length(size_of::<inet_diag_req_v2>() as u32),
        nlmsg_type:  SOCK_DIAG_BY_FAMILY,
        nlmsg_flags: NLM_F_REQUEST | NLM_F_DUMP,
        .. Default::default()
    };

    sock.send(&mut [as_iovec(&mut hdr), as_iovec(req)])?;
    sock.recv(|msg: &inet_diag_msg, mut tail: Attrs| {
        let (src, dst) = addrs(msg);
        let mut diag   = Diag {
            family: msg.idiag_family,
            src:    src,
            dst:    dst,
            state:  msg.idiag_state,
            info:   None,
        };

        while let Some((head, data)) = tail.next::<rtattr>() {
            if head.rta_type == INET_DIAG_INFO {
                diag.info = Some(*data.cast());
            }
        }

        Ok(diag)
    })
}


fn addrs(d: &inet_diag_msg) -> (SocketAddr, SocketAddr) {
    unsafe fn addrs_v4(id: &inet_diag_sockid) -> (SocketAddr, SocketAddr) {
        let src = (*(&id.idiag_src as *const _ as *const in_addr)).s_addr.to_be();
        let dst = (*(&id.idiag_dst as *const _ as *const in_addr)).s_addr.to_be();

        let src = SocketAddr::new(IpAddr::V4(Ipv4Addr::from(src)), id.idiag_sport.to_be());
        let dst = SocketAddr::new(IpAddr::V4(Ipv4Addr::from(dst)), id.idiag_dport.to_be());

        (src, dst)
    }

    unsafe fn addrs_v6(id: &inet_diag_sockid) -> (SocketAddr, SocketAddr) {
        let src = (*(&id.idiag_src as *const _ as *const in6_addr)).s6_addr;
        let dst = (*(&id.idiag_dst as *const _ as *const in6_addr)).s6_addr;

        let src = SocketAddr::new(IpAddr::V6(Ipv6Addr::from(src)), id.idiag_sport.to_be());
        let dst = SocketAddr::new(IpAddr::V6(Ipv6Addr::from(dst)), id.idiag_dport.to_be());

        (src, dst)
    }

    match d.idiag_family {
        AF_INET  => unsafe { addrs_v4(&d.id) },
        AF_INET6 => unsafe { addrs_v6(&d.id) },
        _        => unreachable!(),
    }
}