I'm trying to understand the relationship between the Linux iproute2 tools and the kernel's rtnetlink API. The ip command is convenient for configuring interfaces, addresses, routes, and related settings, while NETLINK_ROUTE sockets let programs communicate with the kernel directly.
When does it make sense for a developer or systems engineer to use rtnetlink instead of invoking iproute2? Are the main benefits performance, avoiding fragile text parsing, persistent sockets, and asynchronous network-event notifications? I'm especially interested in custom network daemons, container networking, monitoring, reconciliation systems, and firewall or tunnel management.
How difficult is it to work with messages and attributes such as ifinfomsg and rtattr in C or Go? Would a library such as libnl, libmnl, or Go's vishvananda/netlink be preferable to parsing raw messages, and when might using ip's JSON output still be good enough?
3 Answers
Direct netlink is most useful for a long-running program that needs reliable state inspection, reconciliation, or event-driven updates. Shelling out repeatedly means process creation, command-specific behavior, and output parsing that can change between releases. A daemon can keep a socket open, dump the current kernel state, subscribe to link/address/route notifications, compare that state with its desired configuration, and apply only the necessary changes. That pattern is common in network managers, container networking, tunnel controllers, and firewall orchestration. The performance improvement is usually less important than correctness and avoiding fragile text parsing.
Netlink is powerful but not especially beginner-friendly. Messages contain nested attributes, flags, family-specific structures, and values whose meaning often requires consulting kernel and iproute2 documentation or source code. Notifications can also arrive asynchronously and may not be ordered in the way your application expects, so you generally need an initial state dump followed by subscription and careful resynchronization. A Go library such as vishvananda/netlink provides a practical middle ground; in C, libmnl or libnl can reduce the amount of raw attribute handling. Raw sockets make sense when you need an operation the libraries do not support or want tight control over behavior.
A typical example is tracking a tunnel: receive link changes, determine whether it is present and operational, notice address assignment or removal, and react immediately. The same model works for monitoring interfaces and routes or managing nftables through its netlink interface.
Use the highest-level interface that meets the requirements. Invoking ip is perfectly reasonable for an administrator script or an infrequent setup action, especially if you can use JSON output and validate errors carefully. A library is better for a service that must be portable, efficient, and continuously connected to the kernel. Direct rtnetlink is not automatically faster in a way users will notice; its main advantages are persistent communication, structured data, event subscriptions, and access to operations that command-line tools may not cover. For traffic control, filtering, or packet-processing features, choose the relevant kernel API rather than assuming rtnetlink alone covers every subsystem.

For simpler tools, ip can emit JSON, which is much safer to consume than its normal human-readable output. I’d start with that unless the program needs continuous notifications, atomic-ish reconciliation, or functionality that ip does not expose.