netlink-packet-route

Typed Rust structs for encoding and decoding Linux rtnetlink route-protocol packets.

Library
Cargo
v0.33.0
73stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
81/100Excellent
Development Activity100
Maintenance96
Community56
Maturity52
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture80
Code Quality82
Innovation68
Learning Curve62

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 a Vec of attribute (NLA) enum variants.
  • A top-level RouteNetlinkMessage enum 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/NetlinkDeserializable trait implementations from netlink-packet-core, so messages plug directly into NetlinkMessage<RouteNetlinkMessage> for socket send/receive.
  • Cross-platform AddressFamily handling with separate implementations gated by cfg for 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 raw netlink-sys socket and round-trip real packets against a live kernel.

Common Use Cases

  • Building a synchronous or async netlink client (as rtnetlink does) 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 the tc binary.
  • 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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search