Expand description
Async netlink library for Linux network configuration.
This crate provides a complete netlink implementation for programmatic network management on Linux. It supports RTNetlink (routing), traffic control, socket diagnostics, and TUN/TAP device management.
§Features
sockdiag- Socket diagnostics via NETLINK_SOCK_DIAGtuntap- TUN/TAP device managementtuntap-async- Async TUN/TAP support (impliestuntap)tc- Traffic control utilitiesoutput- JSON/text output formattingfull- All features enabled
§Example
use nlink::netlink::{Connection, Route};
#[tokio::main]
async fn main() -> nlink::netlink::Result<()> {
let conn = Connection::<Route>::new()?;
// Query interfaces
let links = conn.get_links().await?;
for link in &links {
// Use name_or() helper for cleaner code
println!("{}: {}", link.ifindex(), link.name_or("?"));
}
// Build ifindex -> name map for resolving routes/addresses
let names = conn.get_interface_names().await?;
Ok(())
}§Link State Management
use nlink::netlink::{Connection, Route};
let conn = Connection::<Route>::new()?;
// Bring an interface up
conn.set_link_up("eth0").await?;
// Bring it down
conn.set_link_down("eth0").await?;
// Set MTU
conn.set_link_mtu("eth0", 9000).await?;§Network Namespace Support
Operations can be performed in specific network namespaces:
use nlink::netlink::{Connection, Route, Generic};
use nlink::netlink::namespace;
// Connect to a named namespace (created via `ip netns add myns`)
// Functions are generic over protocol type
let conn: Connection<Route> = namespace::connection_for("myns")?;
let links = conn.get_links().await?;
// Or connect to a container's namespace
let conn: Connection<Route> = namespace::connection_for_pid(container_pid)?;
let links = conn.get_links().await?;
// Or use a path directly
let conn: Connection<Route> = namespace::connection_for_path("/proc/1234/ns/net")?;
// Generic connections work too (e.g., for WireGuard in a namespace)
let genl: Connection<Generic> = namespace::connection_for("myns")?;§Namespace safety — _by_index vs _by_name
Every resource lookup that takes an interface comes in two
flavors: *_by_index(ifindex: u32) and *_by_name(name: &str).
The _by_index form takes a kernel ifindex, which is always
relative to the connection’s netns — safe to call from any
process mount namespace. The _by_name form goes through
Connection::get_link_by_name(...) which resolves via
RTM_GETLINK over the same socket (Plan 192 D4 corrected an
earlier _by_name reads /sys/class/net/ design — both variants
are now netlink-correct; the difference is ergonomic, not a
namespace-safety footgun).
For namespace-aware code, the canonical pattern is:
use nlink::{Connection, Route};
let conn = Connection::<Route>::new()?;
// One name resolution at startup, then ifindex everywhere:
let eth0_idx = conn.get_link_by_name("eth0").await?
.ok_or(nlink::Error::InterfaceNotFound { name: "eth0".into() })?
.ifindex();
conn.set_link_mtu_by_index(eth0_idx, 9000).await?;This is a deliberate design choice. neli and
vishvananda/netlink both leave namespace handling to the
caller — a documented footgun in
Cilium issue #40280.
nlink’s typed InterfaceRef::Index(u32) plus the per-method
_by_index variants make namespace-correct code natural to
write, while _by_name stays available as the deliberate
convenience choice.
§Event Monitoring
Use Connection::subscribe() to select event types, then events() to get a stream:
use nlink::netlink::{Connection, Route, RtnetlinkGroup, NetworkEvent};
use tokio_stream::StreamExt;
let mut conn = Connection::<Route>::new()?;
conn.subscribe(&[RtnetlinkGroup::Link, RtnetlinkGroup::Ipv4Addr])?;
let mut events = conn.events();
while let Some(event) = events.next().await {
match event? {
NetworkEvent::NewLink(link) => println!("New link: {:?}", link.name),
NetworkEvent::NewAddress(addr) => println!("New address: {:?}", addr.address()),
_ => {}
}
}§Multi-Namespace Event Monitoring
Use tokio_stream::StreamMap to monitor multiple namespaces:
use nlink::netlink::{Connection, Route, RtnetlinkGroup};
use tokio_stream::{StreamExt, StreamMap};
let mut streams = StreamMap::new();
let mut conn1 = Connection::<Route>::new()?;
conn1.subscribe_all()?;
streams.insert("default", conn1.into_events());
let mut conn2 = Connection::<Route>::new_in_namespace("ns1")?;
conn2.subscribe_all()?;
streams.insert("ns1", conn2.into_events());
while let Some((ns, event)) = streams.next().await {
println!("[{}] {:?}", ns, event?);
}Re-exports§
pub use netlink::bridge_vlan::BridgeVlanBuilder;pub use netlink::bridge_vlan::BridgeVlanEntry;pub use netlink::bridge_vlan::BridgeVlanFlags;pub use netlink::diagnostics::Bottleneck;pub use netlink::diagnostics::BottleneckType;pub use netlink::diagnostics::ConnectivityReport;pub use netlink::diagnostics::DiagnosticReport;pub use netlink::diagnostics::Diagnostics;pub use netlink::diagnostics::DiagnosticsConfig;pub use netlink::diagnostics::InterfaceDiag;pub use netlink::diagnostics::Issue;pub use netlink::diagnostics::IssueCategory;pub use netlink::diagnostics::IssueStream;pub use netlink::diagnostics::LinkRates;pub use netlink::diagnostics::RouteDiag;pub use netlink::diagnostics::RouteInfo;pub use netlink::diagnostics::Severity;pub use netlink::diagnostics::TcDiag;pub use netlink::fdb::FdbEntry;pub use netlink::fdb::FdbEntryBuilder;pub use netlink::impair::PeerImpairment;pub use netlink::impair::PeerMatch;pub use netlink::impair::PerPeerImpairer;pub use netlink::messages::AddressMessage;pub use netlink::messages::ClassMessage;pub use netlink::messages::FilterMessage;pub use netlink::messages::LinkMessage;pub use netlink::messages::NeighborMessage;pub use netlink::messages::QdiscMessage;pub use netlink::messages::RouteMessage;pub use netlink::messages::RuleMessage;pub use netlink::messages::TcMessage;pub use netlink::parse_params::ParseParams;pub use netlink::tc_handle::FilterPriority;pub use netlink::tc_handle::TcHandle;pub use netlink::tc_handle::TcHandleParseError;pub use netlink::tc_recipe::ReconcileOptions;pub use netlink::tc_recipe::ReconcileReport;pub use netlink::tc_recipe::StaleObject;pub use netlink::tc_recipe::UnmanagedObject;pub use netlink::Connection;pub use netlink::Error;pub use netlink::NamespaceSpec;pub use netlink::NetworkEvent;pub use netlink::Protocol;pub use netlink::Result;pub use netlink::RtnetlinkGroup;pub use netlink::ChainWalk;pub use netlink::EventSource;pub use netlink::EventSubscription;pub use netlink::OwnedEventStream;pub use netlink::Generic;pub use netlink::Nftables;pub use netlink::Route;pub use netlink::Wireguard;pub use util::Bytes;pub use util::Percent;pub use util::Rate;pub use netlink::route::Ipv4Route;pub use netlink::route::Ipv6Route;pub use netlink::route::NextHop;pub use netlink::route::RouteConfig;pub use netlink::route::RouteMetrics;pub use netlink::addr::AddressConfig;pub use netlink::addr::Ipv4Address;pub use netlink::addr::Ipv6Address;pub use netlink::rule::RuleBuilder;pub use netlink::link::LinkConfig;pub use netlink::neigh::NeighborConfig;pub use netlink::pool::ConnectionPool;pub use netlink::pool::ConnectionPoolBuilder;pub use netlink::pool::PooledConnection;pub use netlink::resync::ConnectionFactory;pub use netlink::resync::ConnectionFuture;pub use netlink::resync::ResyncMarker;pub use netlink::resync::ResyncStream;pub use netlink::resync::ResyncedEvent;pub use netlink::resync::events_with_resync;pub use netlink::dump_stream::DumpStream;pub use netlink::nftables::Flowtable;pub use netlink::nftables::config::DeclaredChain;pub use netlink::nftables::config::DeclaredFlowtable;pub use netlink::nftables::config::DeclaredRule;pub use netlink::nftables::config::DeclaredTable;pub use netlink::nftables::config::NftablesConfig;pub use netlink::nftables::config::NftablesDiff;pub use netlink::xfrm::XfrmOffloadFlag;pub use netlink::xfrm::XfrmUserOffload;
Modules§
- facade
- High-level facade APIs — one-line entry points for the 90% case (Plan 200).
- lab
- Lab / integration-test helpers for building ephemeral network environments.
- macros
- Re-exports + runtime substrate for the proc-macro derives
from
nlink-macros. - netlink
- Async netlink protocol implementation for Linux.
- output
- Output formatting (JSON/text) for nlink.
- prelude
- Convenience re-exports for common types.
- sockdiag
- Socket diagnostics library for Linux.
- tuntap
- TUN/TAP device management library.
- util
- Shared utilities for nlink.
Macros§
- require_
module - Macro that returns early with
Ok(())if the named kernel module isn’t loaded or built-in. Use this in integration tests that depend on a specific kernel feature (nf_conntrack,cls_flower,xfrm_user, etc.) so a missing module produces a clean skip rather than a crypticis_not_supported()error deep in the test body. - require_
module_ void - Like
require_modulebut for test functions whose return type is()rather thanResult<()>. - require_
modules - Require multiple kernel modules in one call. Skips the test
(returning
Ok(())) if any of the named modules is not loaded or built-in. Convenience for tests that touch several independent kernel features. - require_
root - Macro that returns early with
Ok(())if the current process is not running as root, useful for integration tests that needCAP_SYS_ADMIN. - require_
root_ void - Like
crate::require_root!but for test functions whose return type is()rather thanResult<()>. - require_
writable_ sysctl - Require that a sysctl path exists and is writable. Used by
sysctl write tests that need
/proc/sys/...to be RW (some container configurations mount it read-only). Skips the test (returningOk(())) if the path is not writable.