use nlink::netlink::{
Connection, Route,
config::{ApplyOptions, NetworkConfig},
};
#[tokio::main]
async fn main() -> nlink::Result<()> {
let conn = Connection::<Route>::new()?;
let cfg = NetworkConfig::new()
.link("declarative_dummy0", |l| l.dummy().mtu(9000).up())
.link("declarative_dummy1", |l| l.dummy().up())
.address("declarative_dummy0", "10.99.0.1/24")?
.route("10.99.1.0/24", |r| r.via("10.99.0.254"))?
.qdisc("declarative_dummy0", |q| q.netem().delay_ms(10));
let diff = match cfg.diff(&conn).await {
Ok(d) => d,
Err(e) if e.is_permission_denied() => {
eprintln!(
"EPERM: declarative apply requires CAP_NET_ADMIN; \
re-run with sudo for the full demo (build-only \
succeeded — the API surface is exercised)."
);
return Ok(());
}
Err(e) => return Err(e),
};
println!(
"initial diff: +{} links, +{} addrs, +{} routes, +{} qdiscs",
diff.links_to_add.len(),
diff.addresses_to_add.len(),
diff.routes_to_add.len(),
diff.qdiscs_to_add.len(),
);
let report = cfg.apply(&conn).await?;
println!("\napplied {} change(s)", report.changes_made);
if !report.is_success() {
eprintln!("apply surfaced {} error(s):", report.errors.len());
for e in &report.errors {
eprintln!(" {e}");
}
}
let reapply = cfg.diff(&conn).await?;
assert!(
reapply.is_empty(),
"idempotent re-apply should produce empty diff; got: \
+{} links, +{} addrs, +{} routes, +{} qdiscs",
reapply.links_to_add.len(),
reapply.addresses_to_add.len(),
reapply.routes_to_add.len(),
reapply.qdiscs_to_add.len(),
);
println!("reapply diff: empty — idempotent ✓");
let updated = NetworkConfig::new()
.link("declarative_dummy0", |l| l.dummy().mtu(9000).up())
.link("declarative_dummy1", |l| l.dummy().up())
.address("declarative_dummy0", "10.99.0.1/24")?
.route("10.99.1.0/24", |r| r.via("10.99.0.254"))?
.route("10.99.2.0/24", |r| r.via("10.99.0.254"))? .qdisc("declarative_dummy0", |q| q.netem().delay_ms(10));
let mut_diff = updated.diff(&conn).await?;
println!(
"\nafter +1 route: +{} routes (expected 1)",
mut_diff.routes_to_add.len(),
);
let report = updated.apply(&conn).await?;
println!("applied {} change(s)", report.changes_made);
let teardown = NetworkConfig::new();
let report = teardown
.apply_with_options(
&conn,
ApplyOptions {
purge: true,
..Default::default()
},
)
.await?;
println!(
"\nteardown applied {} change(s) — demo complete",
report.changes_made,
);
Ok(())
}