[][src]Crate neli

Type safety for the weary netlink user

Rationale

This crate aims to be a pure Rust implementation that defines the necessary constants and wraps them in enums to distinguish between various categories of constants in the context of netlink.

The project is broken down into the following modules:

  • attr - This defines a generic interface for netlink attributes (both generic and routing netlink attributes).
  • consts - This is where all of the C-defined constants are wrapped into type safe enums for use in the library.
  • err - This module contains all of the protocol and library-level errors encountered in the code.
  • genl - This code provides parsing for the generic netlink subsystem of the netlink protocol.
  • nl - This is the top level netlink header code that handles the header that all netlink messages are encapsulated in.
  • rtnl - This module is for the routing netlink subsystem of the netlink protocol.
  • socket - This provides a socket structure for use in sending and receiving messages and a number of convenience functions for commonly encountered use cases.

Nl trait

lib.rs at the top level contains the Nl trait which provides buffer size calculation functions, a serialization method, and a deserialization method. It also contains implementations of Nl for common types.

Design decisions

This is a fairly low level library that currently does not have a whole lot of higher level handle-type data structures and relies mostly on the NlSocket and NlSocketHandle structs to provide most of the convenience functions. I hope to add a higher level API sometime in the v0.5.x releases to ease some of the workflows that have been brought to my attention.

The goal of this library is completeness for handling netlink and am working to incorporate features that will make this library easier to use in all use cases. If you have a use case you would like to see supported, please open an issue on Github.

Examples

Examples of working code exist in the examples/ subdirectory on Github. They have a separate Cargo.toml file to provide easy testing and use.

Workflows seem to usually follow a pattern of socket creation,and then either sending and receiving messages in request/response formats:

use neli::{
    consts::{genl::*, nl::*, socket::*},
    err::NlError,
    genl::{Genlmsghdr, Nlattr},
    nl::{Nlmsghdr, NlPayload},
    socket::NlSocketHandle,
    types::{Buffer, GenlBuffer},
    utils::U32Bitmask,
};

const GENL_VERSION: u8 = 1;

fn request_response() -> Result<(), NlError> {
    let mut socket = NlSocketHandle::connect(
        NlFamily::Generic,
        None,
        U32Bitmask::empty(),
    )?;

    let attrs: GenlBuffer<Index, Buffer> = GenlBuffer::new();
    let genlhdr = Genlmsghdr::new(
        CtrlCmd::Getfamily,
        GENL_VERSION,
        attrs,
    );
    let nlhdr = {
        let len = None;
        let nl_type = GenlId::Ctrl;
        let flags = NlmFFlags::new(&[NlmF::Request, NlmF::Dump]);
        let seq = None;
        let pid = None;
        let payload = NlPayload::Payload(genlhdr);
        Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
    };
    socket.send(nlhdr)?;
     
    // Do things with multi-message response to request...
    let mut iter = socket.iter::<Genlmsghdr<CtrlCmd, CtrlAttr>>(false);
    while let Some(Ok(response)) = iter.next() {
        // Do things with response here...
    }
     
    // Or get single message back...
    let msg = socket.recv::<Nlmsg, Genlmsghdr<CtrlCmd, CtrlAttr>>()?;

    Ok(())
}

or a subscriptions to a stream of event notifications from netlink:

use std::error::Error;

use neli::{
    consts::{genl::*, socket::*},
    err::NlError,
    genl::Genlmsghdr,
    socket,
    utils::{U32BitFlag, U32Bitmask},
};

fn subscribe_to_mcast() -> Result<(), Box<dyn Error>> {
    let mut s = socket::NlSocketHandle::connect(
        NlFamily::Generic,
        None,
        U32Bitmask::empty(),
    )?;
    let id = s.resolve_nl_mcast_group(
        "my_family_name",
        "my_multicast_group_name",
    )?;
    s.add_mcast_membership(U32Bitmask::from(U32BitFlag::new(id)?))?;
    for next in s.iter::<Genlmsghdr<u8, u16>>(true) {
        // Do stuff here with parsed packets...
     
        // like printing a debug representation of them:
        println!("{:?}", next?);
    }

    Ok(())
}

Documentation

Each module has been documented extensively to provide information on how to use the code contained in the module. Pull requests for documentation mistakes, updates, and rewording for clarity is a valuable contribution as this project aims to be as simple to use as possible.

Modules

attr

Shared attribute code for all types of netlink attributes.

consts

High level notes

err

This is the module that contains the error types used in neli

genl

This module contains generic netlink parsing data structures. This is all handled by the Genlmsghdr header struct which contains all of the information needed for the generic netlink layer.

iter

Module for iteration over netlink responses

nl

This module contains the top level netlink header code. Every netlink message will be encapsulated in a top level Nlmsghdr.

rtnl

This module provides an implementation of routing netlink structures and the routing attributes that are at the end of most routing netlink responses.

socket

This module provides code that glues all of the other modules together and allows message send and receive operations.

types

Module containing various types used across the various netlink structures used in neli.

utils

A module containing utilities for working with constructs like bitflags and other low level operations.

Macros

deserialize

This macro can be used to declaratively define deserialization for a struct.

deserialize_type_size

This macro calculates size from type_size methods and returns an error if type_size evaluates to None.

drive_deserialize

This macro can be used to deserialize a single field in a struct.

drive_serialize

This macro can be used to serialize a single field in a struct.

impl_flags

Implement a container for bit flag enums where the set of flags will be condensed into a single value.

impl_trait

For generating a marker trait that flags a new enum as usable in a field that accepts a generic type. This way, the type parameter can be constrained by a trait bound to only accept enums that implement the marker trait.

impl_var

For naming a new enum, passing in what type it serializes to and deserializes from, and providing a mapping from variants to expressions (such as libc consts) that will ultimately be used in the serialization/deserialization step when sending the netlink message over the wire.

log

Logging mechanism for neli for debugging

serialize

This macro can be used to declaratively define serialization for a struct.

Structs

BeU64

A u64 data type that will always be serialized as big endian

Constants

MAX_NL_LENGTH

Max supported message length for netlink messages supported by the kernel.

Traits

Nl

Trait defining basic actions required for netlink communication. Implementations for basic and neli's types are provided (see below). Create new implementations if you have to work with a Netlink API that uses values of more unusual types.