# nlink
[](https://crates.io/crates/nlink)
[](https://docs.rs/nlink)
[](https://github.com/p13marc/nlink#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
```toml
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](https://docs.rs/crate/nlink/latest/features).
MSRV: Rust 1.95, edition 2024.
## Quick start
```rust
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.
```rust
// 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](docs/library.md) — detailed examples: namespaces, TC, WireGuard,
error handling, concurrency.
- [Cookbook recipes](docs/recipes/README.md) — 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 reference](docs/cli.md) — `ip` and `tc` command coverage.
- [Migration guides](docs/migration_guide/README.md) — per-release upgrade notes.
- [Examples](crates/nlink/examples/README.md) — 40+ runnable demos.
- [docs.rs/nlink](https://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
```bash
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.