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
|
// SPDX-License-Identifier: MIT
#![cfg(feature = "tokio_socket")]
use futures::stream::TryStreamExt;
use netlink_packet_route::{
link::{LinkAttribute, LinkExtentMask},
AddressFamily,
};
#[cfg(not(target_os = "freebsd"))]
use rtnetlink::new_connection;
use rtnetlink::{Error, Handle};
#[cfg(not(target_os = "freebsd"))]
async fn do_it(rt: &tokio::runtime::Runtime) -> Result<(), ()> {
env_logger::init();
let (connection, handle, _) = new_connection().unwrap();
rt.spawn(connection);
// Fetch a link by its index
let index = 1;
println!("*** retrieving link with index {index} ***");
if let Err(e) = get_link_by_index(handle.clone(), index).await {
eprintln!("{e}");
}
// Fetch a link by its name
let name = "lo";
println!("*** retrieving link named \"{name}\" ***");
if let Err(e) = get_link_by_name(handle.clone(), name.to_string()).await {
eprintln!("{e}");
}
// Dump all the links and print their index and name
println!("*** dumping links ***");
if let Err(e) = dump_links(handle.clone()).await {
eprintln!("{e}");
}
// Dump all the bridge vlan information
if let Err(e) = dump_bridge_filter_info(handle.clone()).await {
eprintln!("{e}");
}
Ok(())
}
async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> {
let mut links = handle.link().get().match_index(index).execute();
let msg = if let Some(msg) = links.try_next().await? {
msg
} else {
eprintln!("no link with index {index} found");
return Ok(());
};
// We should have received only one message
assert!(links.try_next().await?.is_none());
for nla in msg.attributes.into_iter() {
if let LinkAttribute::IfName(name) = nla {
println!("found link with index {index} (name = {name})");
return Ok(());
}
}
eprintln!(
"found link with index {index}, but this link does not have a name"
);
Ok(())
}
async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> {
let mut links = handle.link().get().match_name(name.clone()).execute();
if (links.try_next().await?).is_some() {
println!("found link {name}");
// We should only have one link with that name
assert!(links.try_next().await?.is_none());
} else {
println!("no link link {name} found");
}
Ok(())
}
async fn dump_links(handle: Handle) -> Result<(), Error> {
let mut links = handle.link().get().execute();
'outer: while let Some(msg) = links.try_next().await? {
for nla in msg.attributes.into_iter() {
if let LinkAttribute::IfName(name) = nla {
println!("found link {} ({})", msg.header.index, name);
continue 'outer;
}
}
eprintln!("found link {}, but the link has no name", msg.header.index);
}
Ok(())
}
#[cfg(not(target_os = "freebsd"))]
async fn dump_bridge_filter_info(handle: Handle) -> Result<(), Error> {
let mut links = handle
.link()
.get()
.set_filter_mask(AddressFamily::Bridge, vec![LinkExtentMask::Brvlan])
.execute();
'outer: while let Some(msg) = links.try_next().await? {
for nla in msg.attributes.into_iter() {
if let LinkAttribute::AfSpecBridge(data) = nla {
println!(
"found interface {} with AfSpecBridge data {:?})",
msg.header.index, data
);
continue 'outer;
}
}
}
Ok(())
}
#[cfg(target_os = "freebsd")]
fn main() -> () {}
#[cfg(not(target_os = "freebsd"))]
fn main() -> Result<(), String> {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_io()
.build()
.unwrap();
let future = do_it(&rt);
println!("blocking in main");
rt.handle().block_on(future).unwrap();
Ok(())
}
|