1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
//! 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_DIAG
//! - `tuntap` - TUN/TAP device management
//! - `tuntap-async` - Async TUN/TAP support (implies `tuntap`)
//! - `tc` - Traffic control utilities
//! - `output` - JSON/text output formatting
//! - `full` - All features enabled
//!
//! # Example
//!
//! ```ignore
//! 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
//!
//! ```ignore
//! 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:
//!
//! ```ignore
//! 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:
//!
//! ```ignore
//! 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](https://github.com/cilium/cilium/issues/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:
//!
//! ```ignore
//! 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:
//!
//! ```ignore
//! 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?);
//! }
//! ```
// The Plan 154 derive macros generate code referencing
// `::nlink::macros::__rt::*` so the same expansion works
// uniformly from any downstream crate. Inside `nlink` itself
// that path would fail to resolve (no external crate named
// `nlink` from our own perspective); this `extern crate self as
// nlink` aliases the crate to its own external name so generated
// paths work here too. Required for the in-tree derive tests in
// `crate::macros::tests`.
extern crate self as nlink;
// Core modules (always available)
// Feature-gated modules
// Re-export common types at crate root for convenience
// Namespace types
// Event types
// Route protocol multicast groups
// Bridge VLAN types
pub use ;
// Diagnostics types
pub use ;
// FDB types
pub use ;
// Per-peer impairment helper
pub use ;
// Message types (commonly used)
pub use ;
// Sealed trait formalizing the `parse_params` contract on every typed TC
// config. Inherent methods stay; the trait is for generic dispatch.
pub use ParseParams;
// Strongly-typed TC handle and filter priority — use these in new code at
// public boundaries instead of raw &str / u16.
pub use ;
// Reconcile-pattern types shared by recipe helpers.
pub use ;
pub use ;
// Plan 187 §2.2 — chain-walk iterator + convenience over the
// `&dyn Error` source chain that handles `Box<nlink::Error>`
// transparently.
pub use ChainWalk;
// Stream-based event API
pub use ;
// Protocol state types
pub use ;
// Strongly-typed unit types (rate, byte, percent values used at TC API
// boundaries). Use these in new code; the kernel's unit confusion (bits/sec
// vs bytes/sec, decimal vs binary) is handled at type construction.
pub use ;
// ---- Plan 148 §4.3 re-export hygiene ----------------------------------
// The route / address / rule builders and the extension traits users
// implement to add custom link/addr/route/neighbor types. Previously
// reachable only via deep `nlink::netlink::route::Ipv4Route`-style
// paths; surface them at the crate root for shorter imports.
// Route builders + nested types.
pub use ;
// Address builders + extension trait.
pub use ;
// Rule builder.
pub use RuleBuilder;
// Link + neighbor extension traits for custom impl.
pub use LinkConfig;
pub use NeighborConfig;
// Connection pool (Plan 159) — bounded mpsc-channel-backed pool
// for high-fanout consumers.
pub use ;
// ENOBUFS resync helper types (Plan 151) — sum type yielded by a
// resync-aware event consumer, plus boundary markers, plus the
// pre-baked Stream wrapper (Plan 151 closeout). Plan 185 added
// the generic `ConnectionFactory<P>` / `ConnectionFuture<P>` —
// the boxed closure shape protocol-specific resync wrappers
// (e.g. `Connection<Nftables>::into_events_with_resync`) consume
// to materialise a fresh unicast connection on every ENOBUFS.
pub use ;
// Streaming dump API (Plan 149) — yield typed netlink dump
// messages one at a time.
pub use DumpStream;
// nftables flowtable (Plan 150) — flow-table fast path for the
// netfilter conntrack-cached forwarding bypass.
pub use Flowtable;
// Declarative nftables config (Plan 157) — diff + apply for
// tables/chains/rules/flowtables, mirroring NetworkConfig's shape.
pub use ;
// XFRM IPsec hardware offload (Plan 153.1) — request kernel push
// SA crypto/packet path onto NIC hardware.
pub use ;
// Plan 189 — `serde` feature flag JSON-shape tests.
// Verifies the kebab-case (struct) / snake_case (enum) JSON
// shape commitments of the gated `Serialize` impls. Gated on
// `cfg(feature = "serde")` so the non-serde build skips them.