netlink-sys
Low-level Rust bindings for Linux netlink sockets, with optional tokio, mio, and async-io integration.
Repository Health
Technical Analysis
netlink-sys provides a thin, safe wrapper around raw Linux netlink sockets — the kernel interface used for routing, network device configuration, audit events, and other kernel/user-space communication. It exposes a synchronous Socket type built directly on top of libc socket calls (bind, connect, send, recv, setsockopt), plus an AsyncSocket trait with pluggable backends for tokio, mio, and smol/async-io so applications can integrate netlink I/O into whichever async runtime they already use.
The crate is deliberately minimal: it does not parse or interpret netlink message payloads, that job belongs to companion crates like netlink-packet-core and netlink-packet-route. Instead it is the foundational transport layer that those higher-level crates, and tools like rtnetlink, are built on top of.
What You Get
- A synchronous
Sockettype wrappinglibc::socket/bind/connect/send/recvwith safe,Result-returning APIs - A
SocketAddrtype modeling the netlink port-number (PID) and multicast group-ID address pair - Socket option helpers for common netlink flags:
NETLINK_CAP_ACK,NETLINK_NO_ENOBUFS,NETLINK_BROADCAST_ERROR,NETLINK_LISTEN_ALL_NSID - An
AsyncSockettrait plusAsyncSocketExtfor ergonomicasync fn-style send/recv, implemented against three optional backends TokioSocket,MioSocket, andSmolSocket— feature-gated async socket implementations for tokio, mio, and async-io/smol respectively- The full table of
NETLINK_*protocol family constants (NETLINK_ROUTE,NETLINK_AUDIT,NETLINK_GENERIC,NETLINK_CRYPTO, and others) re-exported underprotocols
Common Use Cases
- Building or consuming netlink-based network configuration tooling (link/address/route management) alongside
netlink-packet-routeandrtnetlink - Subscribing to Linux audit subsystem events (
NETLINK_AUDIT) for security monitoring or logging daemons - Implementing a custom generic-netlink (
NETLINK_GENERIC) client for a kernel module that exposes its own netlink family - Integrating raw netlink I/O into an async Rust service already built on tokio, mio, or smol without hand-rolling
libcFFI - Writing low-level Linux system daemons (network managers, VPN clients, container networking agents) that need direct kernel socket access
Under The Hood
Architecture netlink-sys sits at the transport layer of the rust-netlink project. Its core, src/socket.rs, defines a Socket(RawFd) newtype that opens AF_NETLINK/SOCK_DGRAM file descriptors directly via libc::socket and implements bind, connect, send/send_to, recv/recv_from, and socket-option get/setters as thin, Result-returning wrappers over the corresponding libc calls; src/addr.rs models the netlink SocketAddr (port number + multicast group ID) used by those calls. On top of this synchronous core, src/async_socket.rs defines an AsyncSocket trait (poll_send, poll_recv, etc.) and src/async_socket_ext.rs layers AsyncSocketExt, whose futures (PollSend, PollRecv, …) turn those poll methods into awaitable calls; src/tokio.rs, src/mio.rs, and src/smol.rs each implement AsyncSocket against their respective reactor, feature-gated behind tokio_socket, mio_socket, and smol_socket. The crate intentionally stops at the transport boundary — it does not parse netlink message payloads, leaving that to sibling crates like netlink-packet-core that build on top of Socket.
Tech Stack The synchronous core has exactly three mandatory dependencies: libc for raw syscalls, bytes for buffer types used in async recv paths, and log for tracing. All async support is additive and optional: tokio (default-features off, only the net feature, for PollEvented), mio (os-poll, os-ext), async-io, and futures-util are pulled in only when their corresponding Cargo feature is enabled, keeping the default build dependency-light. Dev-dependencies (netlink-packet-audit, netlink-packet-core, async-std) are used only for the example binaries under examples/, which demonstrate the sync API, the tokio-backed API, a manual-thread-builder tokio variant, and the async-std/smol variant.
Code Quality src/socket.rs carries a #[cfg(test)] mod test block exercising new, connect, bind, bind_auto, set_non_blocking, and the boolean socket-option round-trips (cap_ack, no_enobufs, broadcast_error); a smaller test module covers src/mio.rs. These are integration-style tests that open real netlink sockets rather than mocking libc, appropriate for a thin FFI wrapper but meaning they only run meaningfully on Linux. The crate uses unsafe in about two dozen places (all syscall/FFI boundary crossings, e.g. libc::socket, libc::bind, libc::setsockopt), each narrowly scoped to a single call rather than wrapping large unsafe blocks. Errors are surfaced as plain std::io::Result/std::io::Error::last_os_error() rather than a custom error enum, keeping the API simple at the cost of losing structured error variants.
API Design The public surface is small and consistent: Socket::new, bind, bind_auto, connect, send/send_to, recv/recv_from mirror standard Rust socket API naming (comparable to std::net::UdpSocket), which minimizes the learning curve for anyone who has used a Rust datagram socket before. The AsyncSocketExt trait provides async fn-shaped methods (send, recv, send_to, recv_from) generated from the lower-level poll_* trait methods, so callers get ergonomic .await-able calls without needing to hand-write Future impls. Documentation is thin outside doc-comments on public items — the README is a two-line pointer to docs.rs, and there is no dedicated usage guide beyond the four examples/ files and the doctest embedded in Socket’s type-level docs.