Skip to main content

Linux Netlink Reference: A Complete Guide

·703 words·4 mins
Ankur Rathore
Author
Ankur Rathore
Senior Systems Engineer pivoting to High-Performance Infrastructure. Building zero-allocation network drivers and cache-friendly data structures.

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() or htonl().

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).

  1. Request: Send a message to GENL_ID_CTRL (16) with command CTRL_CMD_GETFAMILY.
  2. Attribute: Include the name of the subsystem (e.g., "nl80211" or "test1").
  3. Response: The kernel returns a message containing the CTRL_ATTR_FAMILY_ID.
  4. Use: Use that ID in the nlmsg_type for 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_ERROR message. If the error code is 0, 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_DONE message.

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_TRUNC flag in your receiver.

Dump Consistency
#

  • If the kernel’s state changes during a dump, it sets the NLM_F_DUMP_INTR flag.
  • 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
#

  1. neli: High-level, type-safe Netlink for Rust. Great if you don’t want to manage byte alignment manually.
  2. 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 zerocopy or nom for safe parsing of headers.
  • Always validate nlmsg_len to prevent reading past your buffer.

Source: Based on Linux Kernel Documentation (uAPI) and Netlink protocol specifications.