Self-hosted service
vishvananda/netlink avatar
vishvananda/netlink

vishvananda/netlink: a Go wrapper over the Linux netlink socket

Simple netlink library for go.

3,312 stars843 forksGoApache-2.0

At a glance

What is it?
The library maps iproute2-style operations (add a link, add an address, set a master) onto Go functions. It is for Linux-only Go programs that need to configure interfaces, addresses, routes, xfrm, or ethtool state without shelling out to the ip command.
Who is it for?
Adopt vishvananda/netlink if you are writing a Linux-only Go daemon or CNI-style agent that must create links, addresses, routes, or ipsec state in-process and cannot depend on the ip binary being present. Do not adopt it if you need to run on macOS or Windows, or if you want a stable high-level API for every netlink object: the README states that many pieces are not yet fully supported in the high-level interface.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 30 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What vishvananda/netlink replaces, and who needs it

Netlink is the socket family a user-space Linux program uses to talk to the kernel about networking. The README states that netlink communication requires elevated privileges, so in most cases this code needs to be run as root. The raw messages are described in the README as inscrutable at best. This library exists to hide that: it offers an API loosely modeled on the iproute2 CLI, so the shell command ip link add has a counterpart function named AddLink().

The audience is narrow and specific. If you are writing a Go program that runs on Linux and manages network state (a container network plugin, an agent that attaches interfaces to a bridge, a tool that programs ipsec xfrm state) and you do not want to fork out to the ip binary, this is the layer you would sit on. If your program is portable Go that happens to run on Linux, this is the wrong dependency: the package is built around kernel interfaces that only exist there, and the repository carries _unspecified.go files (conntrack_unspecified.go, devlink_unspecified.go, fou_unspecified.go, genetlink_unspecified.go, handle_unspecified.go) whose names indicate non-Linux build targets get a different, reduced implementation rather than the same behaviour.

The project began as a fork of the netlink functionality in docker/libcontainer and was heavily rewritten for testability and to add functionality such as ipsec xfrm handling, according to the README. That lineage explains the shape of the API: it is a working library extracted from a container runtime, not a specification-driven binding.

How the API maps onto netlink messages

The design is a thin, hand-written layer, not code generation. High-level Go structs (Bridge, and the attributes carried in LinkAttrs) are serialized into netlink messages and parsed back out of the replies. The README describes the remaining work in exactly those terms: many of the underlying primitives are present, and adding support is a matter of putting the right fields into the high-level objects and making sure they serialize and deserialize correctly in the Add and List methods.

That sentence is the most useful thing in the README for anyone evaluating coverage. The primitive layer is broader than the object layer. A netlink attribute the kernel supports may still be missing from the corresponding Go struct, and when it is missing you cannot reach it through the high-level call. The README names routing rules and some of the more advanced link types as not yet implemented. So the practical question is never "does netlink support this?" but "does this package's struct for it carry the field?"

The repository layout reflects the same split. Files are paired by concern: addr.go and addr_linux.go, link.go and link_linux.go, filter.go and filter_linux.go, bridge_linux.go, chain_linux.go, class_linux.go. The generic file holds the platform-neutral types, the _linux file holds the implementation, and where a feature has no non-Linux meaning there is an _unspecified.go stub. The cmd/ directory and the nl/ directory referenced by the Makefile's DIRS variable are the other structural hints: a command area and a lower-level package that the top-level package builds on.

Installing the module and creating a bridge

The README gives the module path directly. Add it to a Go module with go get, then import it by its full path.

bash
go get github.com/vishvananda/netlink

Running the test suite is a separate matter, because it touches the kernel. The README states that testing requires root and gives this command:

bash
sudo -E go test github.com/vishvananda/netlink

The Makefile takes a different route to the same privilege requirement. Its test target runs go test with -test.exec sudo, a 60 second timeout, and four parallel tests, and it depends on github.com/vishvananda/netns and golang.org/x/sys/unix being fetched first.

The README's first example creates a bridge named foo and puts eth1 into it. Note the constructor: NewLinkAttrs sets TxQLen to -1 so the kernel picks the default, whereas a plain LinkAttrs{Name: "foo"} leaves TxQLen at 0 unless you set it, which the README calls out explicitly.

go
package main

import (
    "fmt"
    "github.com/vishvananda/netlink"
)

func main() {
    la := netlink.NewLinkAttrs()
    la.Name = "foo"
    mybridge := &netlink.Bridge{LinkAttrs: la}
    err := netlink.LinkAdd(mybridge)
    if err != nil {
        fmt.Printf("could not add %s: %v\n", la.Name, err)
    }
    eth1, _ := netlink.LinkByName("eth1")
    netlink.LinkSetMaster(eth1, mybridge)
}

What you should see is a bridge interface named foo on the host and eth1 enslaved to it, equivalent to ip link add and ip link set master. The second README example is shorter and shows the address path: look up lo with LinkByName, parse a CIDR with ParseAddr, and call AddrAdd. Both examples ignore the error from LinkByName with an underscore, which is fine for a snippet and not fine in an agent that must distinguish "no such interface" from "permission denied".

Root privileges, partial coverage, and the wrong-fit cases

The first limitation is stated plainly: netlink requires elevated privileges, so in most cases this code needs to be run as root. That is not a library defect, it is the kernel's rule, but it shapes deployment. A process using this package needs CAP_NET_ADMIN, and the README's own test instructions use sudo. If your application runs unprivileged or in a container that drops capabilities, the calls will fail at the kernel boundary no matter how the Go code is written.

The second limitation is coverage, and it is the one that bites during implementation rather than deployment. The README's Future Work section says that many pieces of netlink are not yet fully supported in the high-level interface and that aspects of virtually all of the high-level objects do not exist. Routing rules are named as not in place, along with some advanced link types. This is a moving target rather than a fixed hole, but it means you should verify the specific operation you need before designing around the library. Do not assume that because the package handles links and addresses it also handles the queueing discipline, filter, or xfrm attribute you care about.

The third case is portability. Because the implementation files are _linux suffixed and the non-Linux counterparts are _unspecified stubs, a program that compiles on Linux may compile on another platform with different, reduced behaviour. If your code must run on more than one operating system, this library is the wrong tool for the network configuration path; you need a platform abstraction above it, and you should decide that before writing call sites.

How it compares with shelling out to iproute2

The obvious alternative is not another Go library, it is the ip command from iproute2, invoked with os/exec. The difference is in the failure surface. Shelling out gives you the exact CLI semantics, the full set of iproute2 features including the ones this package does not model, and error messages a human can read. It also gives you a dependency on the ip binary being installed in the image, a process spawn per operation, and output you must parse, which changes between iproute2 versions.

In-process netlink calls remove the binary dependency and the parsing. You get typed Go errors and structs instead of text. The cost is that you are now responsible for the parts iproute2 handled for you, and you are limited to the operations the library models. For a long-running agent that reconciles network state on every loop, avoiding a process spawn per check is a real difference. For a one-shot script that runs a handful of commands, ip is simpler and covers more ground.

A second alternative worth naming is golang.org/x/sys/unix, which the module already depends on. That package exposes the raw syscall surface. Using it directly means writing the netlink message construction and attribute parsing yourself, which is precisely the work this library does. It is the right choice only if you need a message shape the library does not model and you are prepared to maintain the encoding. Note that golang.org/x/sys is pinned at v0.10.0 in go.mod, so the raw layer is available at that version.

Maintenance, release cadence and the Apache-2.0 licence

The repository is not archived and the last push was on 2026-08-31, which is recent. The release history is sparser than the commit history: v1.3.1 was tagged on 2025-05-09, v1.3.0 on 2024-08-23, and v1.2.1 the day before that. So the pattern is long stretches between tagged releases with work landing on main in between. If you pin to a tag, you are pinning to a snapshot that may lag the branch by a year or more; if you track main, you inherit unreleased changes. The go.mod declares go 1.23, which sets a floor on the toolchain you build with.

Upgrade cost is mostly the struct surface. Because the high-level objects are hand-written and grow fields as support is added, a minor version bump can change how a struct serializes. The CHANGELOG.md at the repository root is the file to read before bumping, and the Makefile's fmt target (gofmt -l over the test directories) shows the project holds itself to standard formatting, so your own formatting checks will not fight it.

The licence is Apache-2.0, per the LICENSE file at the repository root. That is a permissive licence with an explicit patent grant and a requirement to preserve notices. It is compatible with use in closed-source products. This is a description of the licence text, not legal advice; if you are redistributing the library or a modified version, read the LICENSE file and, where the stakes are high, take your own counsel.

Editorial conclusion

Adopt vishvananda/netlink if you are writing a Linux-only Go daemon or CNI-style agent that must create links, addresses, routes, or ipsec state in-process and cannot depend on the ip binary being present. Do not adopt it if you need to run on macOS or Windows, or if you want a stable high-level API for every netlink object: the README states that many pieces are not yet fully supported in the high-level interface. Before committing, check that the specific object you need (routing rules are named as missing) exists in the package, read the _unspecified.go files to confirm which platforms get stub implementations, and note that the test target requires sudo, so your CI must be able to run privileged tests.

Frequently asked questions

What is vishvananda/netlink used for?

It is a Go library for talking to the Linux kernel over netlink, the interface a user-space program uses to add and remove interfaces, set IP addresses and routes, and configure ipsec. The API is loosely modeled on iproute2, so ip link add corresponds to AddLink().

How do I install vishvananda/netlink?

The README gives go get github.com/vishvananda/netlink as the module path. Running the test suite is separate and requires root, with the README showing sudo -E go test github.com/vishvananda/netlink.

How do I use vishvananda/netlink?

You build a Go struct for the object you want, such as a Bridge with LinkAttrs from NewLinkAttrs, and pass it to the matching function like LinkAdd. Lookups use LinkByName, and addresses go through ParseAddr followed by AddrAdd.

What is netlink in Linux?

Netlink is the socket interface a user-space program in Linux uses to communicate with the kernel. According to the README it requires elevated privileges, so code using it generally runs as root.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. vishvananda/netlink on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vishvananda-netlink.svg)](https://hysenlabs.com/projects/vishvananda-netlink)