diff options
author | HimbeerserverDE <himbeerserverde@gmail.com> | 2022-10-30 23:42:35 +0100 |
---|---|---|
committer | HimbeerserverDE <himbeerserverde@gmail.com> | 2022-10-30 23:42:35 +0100 |
commit | 761b0bed81bcadb407cbc827177b86e5a0cc79fd (patch) | |
tree | 23b1713faadbd0457519f6b96b6b63e19261c098 | |
parent | 17c2a8ad3ce2c11a58e1229ed2c7f8d304a00f1d (diff) |
all addresses of single interface
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | src/lib.rs | 108 |
2 files changed, 120 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..29a7f20 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "linkaddrs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures = "0.3.25" +netlink-packet-route = "0.13.0" +rtnetlink = "0.11.0" +tokio = { version = "1.0.1", features = ["rt-multi-thread"] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f08026b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,108 @@ +use std::fmt; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use futures::future; +use futures::stream::{StreamExt, TryStreamExt}; +use netlink_packet_route::address::Nla::Address; +use netlink_packet_route::rtnl::constants::{AF_INET, AF_INET6}; +use rtnetlink::new_connection; +use tokio::runtime::Runtime; + +#[derive(Debug)] +pub enum Error { + RtNetlink(rtnetlink::Error), + IoError(std::io::Error), + LinkNotFound(String), +} + +impl std::error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::RtNetlink(e) => write!(fmt, "rtnetlink error: {}", e), + Self::IoError(e) => write!(fmt, "rtnetlink connection failed: {}", e), + Self::LinkNotFound(link) => write!(fmt, "link not found: {}", link), + } + } +} + +impl From<rtnetlink::Error> for Error { + fn from(e: rtnetlink::Error) -> Self { + Self::RtNetlink(e) + } +} + +impl From<std::io::Error> for Error { + fn from(e: std::io::Error) -> Self { + Self::IoError(e) + } +} + +type Result<T> = std::result::Result<T, Error>; + +pub fn addresses(link: String) -> Result<Vec<IpAddr>> { + let rt = Runtime::new()?; + + rt.block_on(internal_addresses(link)) +} + +async fn internal_addresses(link: String) -> Result<Vec<IpAddr>> { + let (connection, handle, _) = new_connection()?; + tokio::spawn(connection); + + let mut links = handle + .link() + .get() + .match_name(link.clone()) + .execute(); + + if let Some(link) = links.try_next().await? { + let addrs = handle + .address() + .get() + .set_link_index_filter(link.header.index) + .execute(); + + let addrs = addrs + .map_ok(|v| { + if let Some(Address(bytes)) = v.nlas.first() { + match v.header.family as u16 { + AF_INET => { + let octets: [u8; 4] = (*bytes) + .clone() + .try_into() + .unwrap(); + + let ip = IpAddr::from( + Ipv4Addr::from(octets) + ); + + Some(ip) + } + AF_INET6 => { + let octets: [u8; 16] = (*bytes) + .clone() + .try_into() + .unwrap(); + + let ip = IpAddr::from( + Ipv6Addr::from(octets) + ); + + Some(ip) + } + _ => None + } + } else { + None + } + }) + .try_filter(|v| future::ready(v.is_some())) + .filter_map(|v| future::ready(v.unwrap())); + + Ok(addrs.collect::<Vec<IpAddr>>().await) + } else { + Err(Error::LinkNotFound(link)) + } +} |