From 9f3fc1958b92a7eb3f4d95a071780eac7d4666b5 Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Wed, 3 Jun 2020 15:14:34 +0300 Subject: [PATCH] example: netlink crate --- examples/Cargo.toml | 7 ++++ examples/netlink-route.rs | 83 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 examples/netlink-route.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 1ad5539a..9891a378 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -12,6 +12,9 @@ uds_windows = "0.1.4" inotify = { version = "0.8.2", default-features = false } nix = "0.17.0" timerfd = "1.1.1" +netlink-sys = "0.2" +netlink-packet-core = "0.1" +netlink-packet-route = "0.2" [dev-dependencies] anyhow = "1.0.28" @@ -38,6 +41,10 @@ tokio = { version = "0.2.18", default-features = false } tungstenite = "0.10.1" url = "2.1.1" +[[example]] +name = "netlink-route" +path = "netlink-route.rs" + [[example]] name = "async-h1-client" path = "async-h1-client.rs" diff --git a/examples/netlink-route.rs b/examples/netlink-route.rs new file mode 100644 index 00000000..2323ae77 --- /dev/null +++ b/examples/netlink-route.rs @@ -0,0 +1,83 @@ +use std::io; + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example works only on Linux!"); +} + +#[cfg(target_os = "linux")] +fn main() -> io::Result<()> { + use netlink_packet_core::{NetlinkHeader, NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; + use netlink_packet_route::{rtnl::link::nlas::Nla, LinkMessage, NetlinkPayload, RtnlMessage}; + use netlink_sys::{Protocol, Socket, SocketAddr}; + use smol::Async; + use std::process; + + fn align(len: usize) -> usize { + const RTA_ALIGNTO: usize = 4; + + ((len)+RTA_ALIGNTO-1) & !(RTA_ALIGNTO-1) + } + + smol::run(async { + let mut socket = Socket::new(Protocol::Route)?; + socket.bind(&SocketAddr::new(process::id(), 1))?; + let mut socket = Async::new(socket)?; + + let mut packet = NetlinkMessage { + header: NetlinkHeader { + sequence_number: 1, + flags: NLM_F_DUMP | NLM_F_REQUEST, + ..Default::default() + }, + payload: RtnlMessage::GetLink(LinkMessage::default()).into(), + }; + packet.finalize(); + + let mut buf = vec![0; packet.header.length as usize]; + packet.serialize(&mut buf[..]); + socket.write_with_mut(|sock| sock.send(&buf, 0)).await?; + + 'out: loop { + let mut buf = [0; 4096]; + let mut cursor = 0; + + let (count, _) = socket + .read_with_mut(|sock| sock.recv_from(&mut buf, 0)) + .await?; + loop { + if cursor >= count { + break; + } + let msg_len = { + let mut len_buf = [0; 4]; + len_buf.copy_from_slice(&buf[cursor..cursor + 4]); + u32::from_ne_bytes(len_buf) as usize + }; + let msg_len = align(msg_len); + let reply = NetlinkMessage::::deserialize(&buf[cursor..cursor + msg_len]) + .expect("Failed to deserialize message"); + match reply.payload { + NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)) => { + let name = link + .nlas + .iter() + .filter_map(|nla| match nla { + Nla::IfName(name) => Some(name.clone()), + _ => None, + }) + .next() + .unwrap(); + println!("{}: {}", link.header.index, name); + } + _ => { + break 'out; + } + } + cursor += msg_len; + } + } + + Ok(()) + }) +}