about as good as i want idfk yet
This commit is contained in:
Generated
+1
-1
@@ -772,7 +772,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lda-ipjs"
|
name = "lda-ipjs"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures",
|
"futures",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lda-ipjs"
|
name = "lda-ipjs"
|
||||||
description = "ip -j show schemas"
|
description = "ip -j show schemas"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ pub mod nl;
|
|||||||
pub use crate::subcommands::Backend;
|
pub use crate::subcommands::Backend;
|
||||||
use crate::utils::serialize::mac::option_mac;
|
use crate::utils::serialize::mac::option_mac;
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
use crate::subcommands::link::OperState;
|
||||||
|
|
||||||
/// i dont include what i dont know about (almost all ts)
|
/// i dont include what i dont know about (almost all ts)
|
||||||
#[derive(Serialize, Debug, Deserialize)]
|
#[derive(Serialize, Debug, Deserialize)]
|
||||||
@@ -19,7 +22,7 @@ pub struct AddrOutput {
|
|||||||
pub ifindex: u32,
|
pub ifindex: u32,
|
||||||
pub ifname: String,
|
pub ifname: String,
|
||||||
/// i imagine UP or DOWN, unknown
|
/// i imagine UP or DOWN, unknown
|
||||||
pub operstate: String,
|
pub operstate: OperState,
|
||||||
// 6 has a serde and the enum doesnt? why. (serializing ts is ass although... im not given an array. they string formatted ts)
|
// 6 has a serde and the enum doesnt? why. (serializing ts is ass although... im not given an array. they string formatted ts)
|
||||||
#[serde(with = "option_mac", default)]
|
#[serde(with = "option_mac", default)]
|
||||||
pub address: Option<MacAddr>,
|
pub address: Option<MacAddr>,
|
||||||
@@ -31,18 +34,102 @@ pub struct AddrOutput {
|
|||||||
// Raw JSON shape from ip -j -4 address show
|
// Raw JSON shape from ip -j -4 address show
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct AddrInfo {
|
pub struct AddrInfo {
|
||||||
pub family: Option<String>,
|
pub family: Option<AddressFamily>,
|
||||||
|
|
||||||
pub local: Option<String>,
|
#[serde(flatten, default)]
|
||||||
pub prefixlen: Option<u8>,
|
pub cidr: InterfaceCidr,
|
||||||
|
|
||||||
pub broadcast: Option<String>,
|
pub broadcast: Option<Ipv4Addr>,
|
||||||
|
|
||||||
pub scope: Option<String>,
|
pub scope: Option<String>,
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
// many more exist; we only take what we need
|
// many more exist; we only take what we need
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AddressFamily {
|
||||||
|
Inet,
|
||||||
|
Inet6,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressFamily {
|
||||||
|
pub fn parse_lossy(value: &str) -> Self {
|
||||||
|
match value.to_ascii_lowercase().as_str() {
|
||||||
|
"inet" => Self::Inet,
|
||||||
|
"inet6" => Self::Inet6,
|
||||||
|
_ => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Inet => "inet",
|
||||||
|
Self::Inet6 => "inet6",
|
||||||
|
Self::Other => "other",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for AddressFamily {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for AddressFamily {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?;
|
||||||
|
Ok(Self::parse_lossy(&value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct InterfaceCidr {
|
||||||
|
pub local: Option<IpAddr>,
|
||||||
|
pub prefixlen: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterfaceCidr {
|
||||||
|
pub fn is_complete(&self) -> bool {
|
||||||
|
self.local.is_some() && self.prefixlen.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddrInfo {
|
||||||
|
pub fn local_addr(&self) -> Option<IpAddr> {
|
||||||
|
self.cidr.local
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prefixlen(&self) -> Option<u8> {
|
||||||
|
self.cidr.prefixlen
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_ipv4(&self) -> bool {
|
||||||
|
matches!(self.family, Some(AddressFamily::Inet))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_ipv6(&self) -> bool {
|
||||||
|
matches!(self.family, Some(AddressFamily::Inet6))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddrOutput {
|
||||||
|
pub fn ipv4_addrs(&self) -> impl Iterator<Item = &AddrInfo> {
|
||||||
|
self.addr_info.iter().filter(|info| info.is_ipv4())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipv6_addrs(&self) -> impl Iterator<Item = &AddrInfo> {
|
||||||
|
self.addr_info.iter().filter(|info| info.is_ipv6())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
||||||
get_with_backend(Backend::Json, dev).await
|
get_with_backend(Backend::Json, dev).await
|
||||||
}
|
}
|
||||||
@@ -57,3 +144,45 @@ pub async fn get_with_backend(
|
|||||||
Backend::Netlink => nl::get(dev).await,
|
Backend::Netlink => nl::get(dev).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
use super::{AddrInfo, AddrOutput, AddressFamily};
|
||||||
|
use crate::subcommands::link::OperState;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn address_family_parses_known_values() {
|
||||||
|
assert_eq!(AddressFamily::parse_lossy("inet"), AddressFamily::Inet);
|
||||||
|
assert_eq!(AddressFamily::parse_lossy("INET6"), AddressFamily::Inet6);
|
||||||
|
assert_eq!(AddressFamily::parse_lossy("weird"), AddressFamily::Other);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn addr_info_deserializes_typed_ip_fields() {
|
||||||
|
let info: AddrInfo = serde_json::from_str(
|
||||||
|
r#"{"family":"inet","local":"192.168.1.1","prefixlen":24,"broadcast":"192.168.1.255","scope":"global"}"#,
|
||||||
|
)
|
||||||
|
.expect("addr_info json should deserialize");
|
||||||
|
|
||||||
|
assert_eq!(info.family, Some(AddressFamily::Inet));
|
||||||
|
assert_eq!(
|
||||||
|
info.local_addr(),
|
||||||
|
Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
|
||||||
|
);
|
||||||
|
assert_eq!(info.prefixlen(), Some(24));
|
||||||
|
assert_eq!(info.broadcast, Some(Ipv4Addr::new(192, 168, 1, 255)));
|
||||||
|
assert!(info.cidr.is_complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn addr_output_deserializes_typed_operstate() {
|
||||||
|
let output: AddrOutput = serde_json::from_str(
|
||||||
|
r#"{"ifindex":2,"ifname":"br-lan","operstate":"UP","address":"aa:bb:cc:dd:ee:ff","addr_info":[]}"#,
|
||||||
|
)
|
||||||
|
.expect("addr_output json should deserialize");
|
||||||
|
|
||||||
|
assert_eq!(output.operstate, OperState::Up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,109 @@
|
|||||||
//! i said i aint doing ts no more why am i still here
|
//! i said i aint doing ts no more why am i still here
|
||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
use rtnetlink::Handle;
|
||||||
|
use rtnetlink::packet_route::{AddressFamily, address::AddressAttribute};
|
||||||
|
|
||||||
use crate::subcommands::address::AddrOutput;
|
use crate::subcommands::{
|
||||||
|
address::{AddrInfo, AddrOutput, AddressFamily as IpAddressFamily, InterfaceCidr},
|
||||||
|
link,
|
||||||
|
};
|
||||||
|
|
||||||
// shit this one is even worse you needa collect info from two places
|
|
||||||
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
||||||
let (conn, handle, _) = rtnetlink::new_connection()?;
|
let (conn, handle, _) = rtnetlink::new_connection()?;
|
||||||
tokio::spawn(conn); // every time?
|
tokio::spawn(conn);
|
||||||
let mut address = handle.address().get();
|
|
||||||
let mut link = handle.link().get();
|
get_with_handle(&handle, dev).await
|
||||||
if let Some(dev) = dev {
|
}
|
||||||
link = link.match_name(dev.to_owned());
|
|
||||||
if let Some(ind) = link.execute().try_next().await?.map(|a| a.header.index) {
|
pub async fn get_with_handle(
|
||||||
address = address.set_link_index_filter(ind);
|
handle: &Handle,
|
||||||
};
|
dev: Option<&str>,
|
||||||
address.execute().try_next().await?;
|
) -> anyhow::Result<Vec<AddrOutput>> {
|
||||||
} else {
|
let links = link::nl::get_with_handle(handle, dev).await?;
|
||||||
};
|
let link_by_index: BTreeMap<u32, crate::subcommands::link::LinkOutput> =
|
||||||
todo!();
|
links.into_iter().map(|link| (link.ifindex, link)).collect();
|
||||||
|
|
||||||
|
let mut address = handle.address().get();
|
||||||
|
if let Some(dev) = dev {
|
||||||
|
if let Some(index) = link_by_index
|
||||||
|
.values()
|
||||||
|
.find(|link| link.ifname == dev)
|
||||||
|
.map(|link| link.ifindex)
|
||||||
|
{
|
||||||
|
address = address.set_link_index_filter(index);
|
||||||
|
} else {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stream = address.execute();
|
||||||
|
let mut addr_info_by_index: BTreeMap<u32, Vec<AddrInfo>> = BTreeMap::new();
|
||||||
|
|
||||||
|
while let Some(msg) = stream.try_next().await? {
|
||||||
|
if !matches!(
|
||||||
|
msg.header.family,
|
||||||
|
AddressFamily::Inet | AddressFamily::Inet6
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let family = match msg.header.family {
|
||||||
|
AddressFamily::Inet => Some(IpAddressFamily::Inet),
|
||||||
|
AddressFamily::Inet6 => Some(IpAddressFamily::Inet6),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut local = None;
|
||||||
|
let mut prefixlen = Some(msg.header.prefix_len);
|
||||||
|
let mut broadcast = None;
|
||||||
|
let mut scope = Some(format!("{:?}", msg.header.scope).to_lowercase());
|
||||||
|
let mut label = None;
|
||||||
|
|
||||||
|
for attr in msg.attributes {
|
||||||
|
match attr {
|
||||||
|
AddressAttribute::Address(addr) | AddressAttribute::Local(addr) => {
|
||||||
|
if local.is_none() {
|
||||||
|
local = addr.to_string().parse().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddressAttribute::Broadcast(addr) => {
|
||||||
|
broadcast = addr.to_string().parse().ok();
|
||||||
|
}
|
||||||
|
AddressAttribute::Label(name) => {
|
||||||
|
label = Some(name);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_info_by_index
|
||||||
|
.entry(msg.header.index)
|
||||||
|
.or_default()
|
||||||
|
.push(AddrInfo {
|
||||||
|
family,
|
||||||
|
cidr: InterfaceCidr {
|
||||||
|
local,
|
||||||
|
prefixlen: prefixlen.take(),
|
||||||
|
},
|
||||||
|
broadcast,
|
||||||
|
scope: scope.take(),
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let out = link_by_index
|
||||||
|
.into_values()
|
||||||
|
.map(|link| AddrOutput {
|
||||||
|
ifindex: link.ifindex,
|
||||||
|
ifname: link.ifname,
|
||||||
|
operstate: link.operstate.unwrap_or(link::OperState::Unknown),
|
||||||
|
address: link.address,
|
||||||
|
addr_info: addr_info_by_index.remove(&link.ifindex).unwrap_or_default(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,99 @@ pub mod nl;
|
|||||||
pub use crate::subcommands::Backend;
|
pub use crate::subcommands::Backend;
|
||||||
use crate::utils::serialize::mac::option_mac;
|
use crate::utils::serialize::mac::option_mac;
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
use serde::{Deserialize, Serialize};
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
|
use rtnetlink::packet_route::link::State as NetlinkOperState;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct LinkOutput {
|
pub struct LinkOutput {
|
||||||
pub ifindex: u32,
|
pub ifindex: u32,
|
||||||
pub ifname: String,
|
pub ifname: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub operstate: Option<String>,
|
pub operstate: Option<OperState>,
|
||||||
#[serde(default, with = "option_mac")]
|
#[serde(default, with = "option_mac")]
|
||||||
pub address: Option<MacAddr>,
|
pub address: Option<MacAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum OperState {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Unknown,
|
||||||
|
Dormant,
|
||||||
|
LowerLayerDown,
|
||||||
|
NotPresent,
|
||||||
|
Testing,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OperState {
|
||||||
|
pub fn parse_lossy(value: &str) -> Self {
|
||||||
|
match value.to_ascii_uppercase().as_str() {
|
||||||
|
"UP" => Self::Up,
|
||||||
|
"DOWN" => Self::Down,
|
||||||
|
"UNKNOWN" => Self::Unknown,
|
||||||
|
"DORMANT" => Self::Dormant,
|
||||||
|
"LOWERLAYERDOWN" | "LOWER_LAYER_DOWN" | "LOWERLAYER_DOWN" => Self::LowerLayerDown,
|
||||||
|
"NOTPRESENT" | "NOT_PRESENT" => Self::NotPresent,
|
||||||
|
"TESTING" => Self::Testing,
|
||||||
|
_ => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Up => "UP",
|
||||||
|
Self::Down => "DOWN",
|
||||||
|
Self::Unknown => "UNKNOWN",
|
||||||
|
Self::Dormant => "DORMANT",
|
||||||
|
Self::LowerLayerDown => "LOWERLAYERDOWN",
|
||||||
|
Self::NotPresent => "NOTPRESENT",
|
||||||
|
Self::Testing => "TESTING",
|
||||||
|
Self::Other => "OTHER",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_up(self) -> bool {
|
||||||
|
matches!(self, Self::Up)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for OperState {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for OperState {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?;
|
||||||
|
Ok(Self::parse_lossy(&value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
|
impl From<NetlinkOperState> for OperState {
|
||||||
|
fn from(value: NetlinkOperState) -> Self {
|
||||||
|
match value {
|
||||||
|
NetlinkOperState::Up => Self::Up,
|
||||||
|
NetlinkOperState::Down => Self::Down,
|
||||||
|
NetlinkOperState::Unknown => Self::Unknown,
|
||||||
|
NetlinkOperState::Dormant => Self::Dormant,
|
||||||
|
NetlinkOperState::LowerLayerDown => Self::LowerLayerDown,
|
||||||
|
NetlinkOperState::NotPresent => Self::NotPresent,
|
||||||
|
NetlinkOperState::Testing => Self::Testing,
|
||||||
|
_ => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<LinkOutput>> {
|
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<LinkOutput>> {
|
||||||
get_with_backend(Backend::Json, dev).await
|
get_with_backend(Backend::Json, dev).await
|
||||||
}
|
}
|
||||||
@@ -33,3 +114,36 @@ pub async fn get_with_backend(
|
|||||||
Backend::Netlink => nl::get(dev).await,
|
Backend::Netlink => nl::get(dev).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{LinkOutput, OperState};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operstate_parses_json_and_netlink_spellings() {
|
||||||
|
assert_eq!(OperState::parse_lossy("UP"), OperState::Up);
|
||||||
|
assert_eq!(OperState::parse_lossy("Up"), OperState::Up);
|
||||||
|
assert_eq!(
|
||||||
|
OperState::parse_lossy("LOWERLAYERDOWN"),
|
||||||
|
OperState::LowerLayerDown
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
OperState::parse_lossy("LowerLayerDown"),
|
||||||
|
OperState::LowerLayerDown
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_output_deserializes_typed_operstate() {
|
||||||
|
let link: LinkOutput = serde_json::from_str(
|
||||||
|
r#"{"ifindex":1,"ifname":"eth0","operstate":"UP","address":"aa:bb:cc:dd:ee:ff"}"#,
|
||||||
|
)
|
||||||
|
.expect("link json should deserialize");
|
||||||
|
|
||||||
|
assert_eq!(link.operstate, Some(OperState::Up));
|
||||||
|
assert_eq!(
|
||||||
|
link.address.map(|mac| mac.to_string()).as_deref(),
|
||||||
|
Some("aa:bb:cc:dd:ee:ff")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
use rtnetlink::Handle;
|
||||||
use rtnetlink::packet_route::link::LinkAttribute;
|
use rtnetlink::packet_route::link::LinkAttribute;
|
||||||
|
|
||||||
use super::LinkOutput;
|
use super::LinkOutput;
|
||||||
@@ -9,6 +10,13 @@ pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<LinkOutput>> {
|
|||||||
let (conn, handle, _) = rtnetlink::new_connection()?;
|
let (conn, handle, _) = rtnetlink::new_connection()?;
|
||||||
tokio::spawn(conn);
|
tokio::spawn(conn);
|
||||||
|
|
||||||
|
get_with_handle(&handle, dev).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_with_handle(
|
||||||
|
handle: &Handle,
|
||||||
|
dev: Option<&str>,
|
||||||
|
) -> anyhow::Result<Vec<LinkOutput>> {
|
||||||
let mut req = handle.link().get();
|
let mut req = handle.link().get();
|
||||||
if let Some(dev) = dev {
|
if let Some(dev) = dev {
|
||||||
req = req.match_name(dev.to_owned());
|
req = req.match_name(dev.to_owned());
|
||||||
@@ -32,7 +40,7 @@ pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<LinkOutput>> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LinkAttribute::OperState(state) => operstate = Some(format!("{state:?}")),
|
LinkAttribute::OperState(state) => operstate = Some(state.into()),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::{
|
|||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
|
use rtnetlink::Handle;
|
||||||
use rtnetlink::packet_route::{
|
use rtnetlink::packet_route::{
|
||||||
AddressFamily,
|
AddressFamily,
|
||||||
neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourState},
|
neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourState},
|
||||||
@@ -27,6 +28,16 @@ pub async fn get(
|
|||||||
let (conn, handle, _) = rtnetlink::new_connection()?;
|
let (conn, handle, _) = rtnetlink::new_connection()?;
|
||||||
tokio::spawn(conn);
|
tokio::spawn(conn);
|
||||||
|
|
||||||
|
get_with_handle(&handle, ips, devs, nuds, macs).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_with_handle(
|
||||||
|
handle: &Handle,
|
||||||
|
ips: &[IpAddr],
|
||||||
|
devs: &[impl AsRef<str>],
|
||||||
|
nuds: &[NUDState],
|
||||||
|
macs: &[MacAddr],
|
||||||
|
) -> anyhow::Result<Vec<NeighborItem>> {
|
||||||
let mut neighbor_data = handle.neighbours().get().execute();
|
let mut neighbor_data = handle.neighbours().get().execute();
|
||||||
|
|
||||||
// Build filter sets (empty = match all)
|
// Build filter sets (empty = match all)
|
||||||
@@ -36,7 +47,7 @@ pub async fn get(
|
|||||||
let mac_set: HashSet<MacAddr> = macs.iter().copied().collect();
|
let mac_set: HashSet<MacAddr> = macs.iter().copied().collect();
|
||||||
|
|
||||||
// Prefetch all links once; link lookups were the ugliest and most expensive part.
|
// Prefetch all links once; link lookups were the ugliest and most expensive part.
|
||||||
let ifname_cache: HashMap<u32, String> = link::nl::get(None)
|
let ifname_cache: HashMap<u32, String> = link::nl::get_with_handle(handle, None)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|link| (link.ifindex, link.ifname))
|
.map(|link| (link.ifindex, link.ifname))
|
||||||
|
|||||||
Reference in New Issue
Block a user