extern crate neli;
use neli::consts::{CtrlAttr, CtrlCmd, GenlId, NlFamily, NlmF, Nlmsg};
use neli::err::NlError;
use neli::genl::Genlmsghdr;
use neli::nl::Nlmsghdr;
use neli::nlattr::Nlattr;
use neli::socket::NlSocket;
use neli::Nl;
use neli::StreamReadBuffer;

const GENL_VERSION: u8 = 2;

// This example attempts to mimic the "genl ctrl list" command. For simplicity, it only outputs
// the name and identifier of each generic netlink family.

fn main() -> Result<(), NlError> {
    let mut socket = NlSocket::connect(NlFamily::Generic, None, None, true)?;

    let attrs: Vec<Nlattr<CtrlAttr>> = vec![];
    let genlhdr = Genlmsghdr::new(CtrlCmd::Getfamily, GENL_VERSION, attrs)?;
    let nlhdr = {
        let len = None;
        let nl_type = GenlId::Ctrl;
        let flags = vec![NlmF::Request, NlmF::Dump];
        let seq = None;
        let pid = None;
        let payload = genlhdr;
        Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
    };
    socket.send_nl(nlhdr)?;

    loop {
        let response = socket.recv_nl::<GenlId, Genlmsghdr<CtrlCmd>>(None)?;

        if let GenlId::UnrecognizedVariant(id) = response.nl_type {
            match Nlmsg::from(id) {
                // This example could be improved by reinterpreting the payload as an Nlmsgerr
                // struct and printing the specific error encountered.
                Nlmsg::Error => panic!("An error occurred while retrieving available families."),
                Nlmsg::Done => break,
                _ => {}
            }
        }

        let mut handle = response.nl_payload.get_attr_handle::<CtrlAttr>();
        handle.parse_nested_attributes()?;

        for attr in handle.iter().unwrap() {
            match &attr.nla_type {
                CtrlAttr::FamilyName => {
                    let mut mem = StreamReadBuffer::new(&attr.payload);
                    mem.set_size_hint(attr.payload.len() - 1);
                    let name = String::deserialize(&mut mem)?;
                    println!("{}", name);
                }
                CtrlAttr::FamilyId => {
                    let mut mem = StreamReadBuffer::new(&attr.payload);
                    let id = u16::deserialize(&mut mem)?;
                    println!("\tID: 0x{:x}", id);
                }
                _ => {}
            }
        }
    }

    Ok(())
}

Add an example that lists genl families
Add an example that lists genl families

Running this example on my Ubuntu 18.04 virtual machine outputs:

$ cargo run --example ctrl-list
nlctrl
	ID: 0x10
VFS_DQUOT
	ID: 0x11
NLBL_MGMT
	ID: 0x13
NLBL_CIPSOv4
	ID: 0x14
NLBL_CALIPSO
	ID: 0x15
NLBL_UNLBL
	ID: 0x16
acpi_event
	ID: 0x17
thermal_event
	ID: 0x18
tcp_metrics
	ID: 0x19
SEG6
	ID: 0x1a
TASKSTATS
	ID: 0x1b
