netlink-packet-route
Typed Rust structs for encoding and decoding Linux rtnetlink route-protocol packets.
Repository Health
Technical Analysis
netlink-packet-route abstracts the Linux kernel’s rtnetlink wire protocol into typed, safe Rust data structures, sparing callers from hand-decoding raw netlink byte buffers. It groups the protocol into modules that mirror familiar ip/tc command surfaces — link, address, route, rule, tc, neighbour, neighbour_table, and nsid — each with its own message, header, and attribute types that implement the Parseable/Emitable traits from netlink-packet-core for serialization and deserialization.
The crate is a foundational building block in the rust-netlink ecosystem: higher-level crates such as rtnetlink wrap it to provide an async, socket-driven API, while this crate itself stays low-level and synchronous, focused purely on packet encoding/decoding rather than socket I/O.
What You Get
- Typed message structs for every rtnetlink object class —
LinkMessage,AddressMessage,RouteMessage,RuleMessage,TcMessage,NeighbourMessage,NeighbourTableMessage,NsidMessage— each pairing a fixed header with aVecof attribute (NLA) enum variants. - A top-level
RouteNetlinkMessageenum that dispatches all RTM_NEW/DEL/GET/SET message kinds (links, addresses, routes, rules, qdiscs, tclasses, tfilters, actions, neighbours, neighbour tables, nsids) to the right typed payload. Parseable/Emitable/NetlinkSerializable/NetlinkDeserializabletrait implementations fromnetlink-packet-core, so messages plug directly intoNetlinkMessage<RouteNetlinkMessage>for socket send/receive.- Cross-platform
AddressFamilyhandling with separate implementations gated bycfgfor Linux/Android/Fuchsia, FreeBSD, and a generic fallback, so the crate builds outside Linux even though most message kinds are Linux-specific. - Unknown-attribute tolerance — unrecognized NLAs decode into
Other(DefaultNla)instead of failing the parse, so newer kernels with extra attributes don’t break older callers. - Seven runnable examples (
dump_packet_links,dump_routes,dump_rules,dump_neighbours,dump_neighbour_tables,dump_packet_link_bridge_vlan,new_rule) that open a rawnetlink-syssocket and round-trip real packets against a live kernel.
Common Use Cases
- Building a synchronous or async netlink client (as
rtnetlinkdoes) that needs typed request/response packets instead of raw byte buffers. - Writing container-networking or CNI tooling that manipulates Linux interfaces, addresses, and routes (equivalent to scripting
ip link/ip addr/ip route). - Implementing traffic-control (
tc) automation — creating qdiscs, classes, and filters programmatically instead of shelling out to thetcbinary. - Reading and validating raw rtnetlink packets captured via
nlmon/tcpdump for debugging or protocol-conformance testing. - Managing network namespaces or neighbour (ARP/NDP) tables from Rust system daemons and network agents.
Under The Hood
Architecture — The crate is organized as one module per rtnetlink object class (link, address, route, rule, tc, neighbour, neighbour_table, nsid, prefix), each exposing a <X>Message struct built from a fixed-size header plus a Vec<XAttribute> of parsed NLAs. src/message.rs is the dispatch hub: it defines RouteNetlinkMessage, an enum with one variant per RTM_* message type (RTM_NEWLINK..RTM_GETACTION etc., all declared as local u16 constants), and implements NetlinkSerializable/NetlinkDeserializable by matching on message_type to route bytes into the right per-module parser. Attribute modules (e.g. link/attribute.rs) implement the Nla/Parseable/Emitable traits from netlink-packet-core per NLA, using IFLA_*-style constants and falling back to DefaultNla for anything unrecognized — this is the crate’s core resilience strategy against newer-kernel attributes it doesn’t yet model. Cross-platform support is handled by three mutually exclusive AddressFamily implementations (address_family_linux.rs, address_family_freebsd.rs, address_family_fallback.rs) selected via cfg(target_os = ...), letting the crate compile (with a reduced surface) on non-Linux hosts. Tech Stack — Pure Rust, edition 2021, MSRV 1.77. Runtime dependencies are minimal and low-level: netlink-packet-core (the shared NLA/Parseable/Emitable trait layer and error types), bitflags for flag enums, libc for raw OS constants, and log for tracing. Dev-dependencies add netlink-sys (raw netlink sockets, used only by the examples) and pretty_assertions for readable test diffs. There is no async runtime dependency — this crate never touches a socket; that responsibility is deliberately left to consumers like rtnetlink. Code Quality — The project enforces a strict no-panic policy for library code (documented in the README: “No panic is allowed, please use Result<> instead of unwrap() or expect()”), backed by CI-run cargo fmt/cargo clippy, and a signed-off-by commit convention. Public types are annotated #[non_exhaustive] to preserve forward API compatibility as new kernel attributes are added. Testing follows a real-packet-capture methodology: unit tests embed raw byte arrays captured via nlmon/tcpdump/Wireshark and assert round-trip parse/emit correctness — there are 83 files under tests/tests.rs modules across the crate’s 252 source files (~38.8k total lines), with unwrap() calls concentrated in that test code rather than library logic. Contribution guidelines explicitly route integration testing (talking to a live kernel) to the downstream rtnetlink crate, keeping this crate’s own test suite hermetic. API Design — The public surface favors explicit, matchable enums (RouteNetlinkMessage::GetLink(LinkMessage)) and Vec<Attribute>-style messages over builder patterns, which is idiomatic for netlink’s own list-of-typed-tuples wire format but does mean callers construct attribute vectors and use .iter().find_map() to extract fields, as shown in every bundled example. Module names deliberately mirror ip/tc subcommands (link, address, route, rule, tc, neighbour), which lowers the learning curve for anyone already familiar with Linux iproute2. Documentation is comment-driven rather than example-doc-driven inside lib.rs, but the seven standalone runnable examples substitute effectively as executable documentation for the socket-integration pattern users actually need.