Linux Netlink Reference: A Complete Guide #
Netlink is the standard Linux kernel interface for communication between the kernel and userspace. It is a socket-based (AF_NETLINK) protocol used for networking configuration, audit logs, and hardware management.
1. Core Principles #
4-Byte Alignment #
Netlink messages must be aligned to a 4-byte boundary.
- A 16-byte message stays as is.
- A 17-byte message must be padded to 20 bytes.
- Rust Tip: When calculating lengths, ensure your buffer logic rounds up:
(len + 3) & !3.
Endianness (Host Byte Order) #
Unlike standard networking (TCP/IP), which uses Big Endian (Network Byte Order), Netlink uses Host Byte Order.
- If your CPU is Little Endian (x86, ARM), the bytes are Little Endian.
- Do not use
htons()orhtonl().
2. Message Format #
A Netlink message consists of a fixed header followed by a payload (usually Attributes).
The Netlink Header (nlmsghdr)
#
struct nlmsghdr {
nlmsg_len: u32, // Length of message including header
nlmsg_type: u16, // Message content (Family ID in Generic Netlink)
nlmsg_flags: u16, // Request/Dump/ACK flags
nlmsg_seq: u32, // Sequence number (tracking number)
nlmsg_pid: u32, // Port ID (usually 0 when talking to kernel)
}The Payload (Attributes / TLVs) #
Data is stored in Type-Length-Value (TLV) format called attributes.
- Type: 16-bit identifier for what the data is.
- Length: 16-bit length (including the attribute header).
- Value: The actual data.
- Note: Attributes themselves must also be 4-byte aligned.
3. Classic vs. Generic Netlink #
| Feature | Classic Netlink | Generic Netlink |
|---|---|---|
| Subsystems | Hardcoded (e.g., NETLINK_ROUTE) |
Dynamic (Registered by name) |
| ID Space | Limited (0-31) | Thousands of dynamic IDs |
nlmsg_type |
Identifies the Command | Identifies the Subsystem |
| Inner Header | None (Fixed structures) | genlmsghdr (Identifies the Command) |
Generic Netlink Header (genlmsghdr)
#
In Generic Netlink, this header follows the nlmsghdr.
struct genlmsghdr {
cmd: u8, // The actual action (GET, SET, etc.)
version: u8, // API version (usually 1)
reserved: u16, // Must be 0
}4. Resolving the Family ID #
Because Generic Netlink IDs are dynamic, you must “lookup” the ID of the subsystem you want to talk to using the Netlink Controller (ID 16).
- Request: Send a message to
GENL_ID_CTRL(16) with commandCTRL_CMD_GETFAMILY. - Attribute: Include the name of the subsystem (e.g.,
"nl80211"or"test1"). - Response: The kernel returns a message containing the
CTRL_ATTR_FAMILY_ID. - Use: Use that ID in the
nlmsg_typefor all future messages to that subsystem.
5. Communication Patterns #
A. The “Do” (Single Action) #
- Goal: Change a setting or get a single object.
- Flags:
NLM_F_REQUEST | NLM_F_ACK. - Response: An
NLMSG_ERRORmessage. If the error code is0, it’s a “Positive ACK” (Success).
B. The “Dump” (Bulk Request) #
- Goal: List all objects (e.g., all IP addresses).
- Flags:
NLM_F_REQUEST | NLM_F_DUMP. - Response: Multiple messages. The end of the list is marked by an
NLMSG_DONEmessage.
C. Multicast (Notifications) #
- Goal: Listen for events (e.g., “Link Down”).
- Setup: Find the Group ID in the Family info and use
setsockopt(..., NETLINK_ADD_MEMBERSHIP). - Note: Use a separate socket for notifications to avoid mixing with request responses.
6. Operational Details #
Buffer Sizing #
- Netlink is Datagram-oriented. If your buffer is too small, the message is truncated and the rest is lost.
- Recommended: Use a 32KB buffer for receiving dumps.
- Check: Always check for the
MSG_TRUNCflag in your receiver.
Dump Consistency #
- If the kernel’s state changes during a dump, it sets the
NLM_F_DUMP_INTRflag. - If you see this, discard the results and retry the dump.
Introspection #
- You can query the kernel for “Policy.”
- Command:
CTRL_CMD_GETPOLICY. - Use this to check if the kernel supports a specific attribute before sending it.
7. Rust Implementation Notes #
If you are implementing this in Rust, keep these tips in mind:
Crates #
neli: High-level, type-safe Netlink for Rust. Great if you don’t want to manage byte alignment manually.netlink-packet-core/netlink-sys: Lower level. Good for high-performance or custom protocol implementations.
Alignment Logic #
When writing raw bytes, ensure you calculate the padding:
fn align(len: usize) -> usize {
(len + 3) & !3
}Safety #
Since Netlink involves parsing byte streams from the kernel:
- Use
zerocopyornomfor safe parsing of headers. - Always validate
nlmsg_lento prevent reading past your buffer.
Source: Based on Linux Kernel Documentation (uAPI) and Netlink protocol specifications.