nlink 0.19.0

Async netlink library for Linux network configuration
Documentation

nlink

Crates.io Documentation License

A Rust library for Linux network configuration via netlink. Async/tokio-native, type-safe, owns its wire format end-to-end (no rtnetlink / netlink-packet-* dependency). CLI binaries (ip, tc, ss, nft, wg, bridge, devlink, ethtool, wifi) ship as proof-of-concept demonstrations of the library.

Install

nlink = "0.19"

Feature flags: sockdiag, tuntap, tuntap-async, output, namespace_watcher, syscall_batch (recvmmsg/sendmmsg batching), serde, lab (test harness), full. Full list in the docs.rs feature table.

MSRV: Rust 1.95, edition 2024.

Quick start

use nlink::netlink::{Connection, Route, RtnetlinkGroup, NetworkEvent};
use tokio_stream::StreamExt;

#[tokio::main]
async fn main() -> nlink::Result<()> {
    let conn = Connection::<Route>::new()?;

    // Query
    for link in conn.get_links().await? {
        println!("{}: {} (up={})", link.ifindex(), link.name_or("?"), link.is_up());
    }

    // Modify
    conn.set_link_up("eth0").await?;
    conn.set_link_mtu("eth0", 9000).await?;

    // Observe
    conn.subscribe(&[RtnetlinkGroup::Link, RtnetlinkGroup::Ipv4Addr])?;
    let mut events = conn.events().await;
    while let Some(event) = events.try_next().await? {
        if let NetworkEvent::NewLink(link) = event {
            println!("link added: {}", link.name_or("?"));
        }
    }
    Ok(())
}

What's in the box

Networking core — links (20+ types: dummy, veth, bridge, bond, VLAN, VXLAN, GRE, IPIP, SIT, VTI, Geneve, netkit, IFB, VRF, MACsec, MACVLAN, IPVLAN, GTP, tun/tap), addresses, routes, neighbors, routing rules, nexthop objects + ECMP groups, MPLS, SRv6, bridge FDB + VLAN filtering, network namespaces.

Traffic control — 18 typed qdisc kinds, typed classes (HTB, HFSC, DRR, QFQ), 9 typed filter kinds, 14 typed action kinds, filter chains, BPF program attachment. Every typed config has a parse_params(&[&str]) so tc CLI syntax round-trips through the typed API. Strongly-typed units: Rate (bytes/sec internally, parses 100mbit / 1gbit), Bytes, Percent, TcHandle, FilterPriority.

Generic Netlink families — WireGuard, MACsec, MPTCP, ethtool, nl80211 (WiFi), devlink, DPLL (clock synchronization — SyncE/PTP/GNSS, kernel 6.7+), net_shaper (TX hardware shaping, kernel 6.13+).

Firewall — nftables tables/chains/rules/sets/NAT/match expressions, flowtables (Expr::FlowOffload), multicast event subscription, atomic single-batch commits via Transaction.

Diagnostics & observability — socket diagnostics (SockDiag), connection tracking (Netfilter/ctnetlink), Linux audit, SELinux events, FIB lookups, ethtool statistics + monitor, kobject uevent (device hotplug), process connector lifecycle events.

Cross-cutting — XFRM IPsec SA/SP management + hardware offload (XFRMA_OFFLOAD_DEV), 30-second default per-Connection timeout (override or opt out), NETLINK_EXT_ACK TLV parsing (kernel error messages with offset + attribute name), NETLINK_GET_STRICT_CHK opt-in.

High-level APIs

The lower-level imperative API is the foundation; these declarative layers collapse common configuration patterns.

// Declarative network state — diff against kernel, apply changes idempotently.
use nlink::netlink::config::NetworkConfig;
NetworkConfig::new()
    .link("br0", |l| l.bridge().up())
    .link("dummy0", |l| l.dummy().mtu(9000).up().master("br0"))
    .address("br0", "192.168.100.1/24")?
    .apply(&conn).await?;

// Declarative nftables ruleset — atomic batch commit.
use nlink::netlink::nftables::config::NftablesConfig;
let cfg = NftablesConfig::new()
    .table("filter", |t| t.inet()
        .chain("input", |c| c.hook_input().policy_drop()
            .rule(|r| r.iif("lo").accept())
            .rule(|r| r.ct_state_established_related().accept())));
cfg.diff(&conn).await?.apply(&conn).await?;

// Rate limiting — typed Rate, no bits-vs-bytes confusion.
use nlink::{Rate, netlink::ratelimit::RateLimiter};
RateLimiter::new("eth0").egress(Rate::mbit(100)).ingress(Rate::mbit(50))
    .apply(&conn).await?;

// Per-peer impairment — netem per destination on shared L2.
use nlink::netlink::impair::PerPeerImpairer;
PerPeerImpairer::new("vethA-br")
    .impair_dst_ip("172.100.3.18".parse()?, /* netem config */)
    .apply(&conn).await?;

// Network diagnostics — find issues, score bottlenecks.
let report = nlink::netlink::diagnostics::Diagnostics::new(conn).scan().await?;

Building blocks for downstream code

  • ConnectionPool<P> — bounded async pool of typed connections; each lease gets its own kernel-side socket, so the kernel processes them in parallel.
  • DumpStream<T>Stream adapter over netlink dumps; O(1) memory on BGP/conntrack/IPsec-scale tables instead of buffering the full response.
  • ResyncStream<T> — wraps multicast subscriptions with ENOBUFS recovery (ResyncedEvent::Marker(ResyncStart) → snapshot replay → ResyncEnd).
  • nlink-macros — declare a custom Generic Netlink family in ~30 lines via #[genl_family] + #[derive(GenlMessage)]; consume through conn.send_typed(req).await? / dump_typed_stream. The in-tree DPLL and net_shaper families are the canonical dogfoods.

Documentation

  • Library guide — detailed examples: namespaces, TC, WireGuard, error handling, concurrency.
  • Cookbook recipes — end-to-end walkthroughs (per-peer impairment, VLAN-aware bridges, bidirectional rate limiting, WireGuard mesh in namespaces, ENOBUFS-resync loops, define-your-own-GENL-family).
  • CLI referenceip and tc command coverage.
  • Migration guides — per-release upgrade notes.
  • Examples — 40+ runnable demos.
  • docs.rs/nlink — full API reference.

Project status

API stable and used in production. Wire format pinned by build-time sizeof(struct …) CI gates; concurrent shared-Arc<Connection> use is safe (serialized via internal mutex — use ConnectionPool for parallel throughput). 14 CI gates on every push (build × 2 feature sets, tests × 2, clippy --deny warnings, doc with strict intra-doc links, semver-checks, public-api diff, machete, msrv, plus 5 nlink-specific audit scripts) + a privileged integration job that runs the root-gated kernel round-trip suite.

Building from source

cargo build --release
cargo run --release -p ip -- link show
cargo run --release -p tc -- qdisc show

License

Apache 2.0 OR MIT, at your option.