whatever
"claude do it" type shi
This commit is contained in:
Generated
+1
-1
@@ -627,7 +627,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lda-ipjs"
|
name = "lda-ipjs"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
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.1"
|
version = "0.0.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
//! this is for link. You need link; at least to build an index -> name map. i Need It. sometimes.
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
//! this is purely experimental. im not doing ts no mo
|
//! rtnetlink-based neighbor table query. One syscall, filter in userspace.
|
||||||
|
|
||||||
// hallo
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
net::IpAddr,
|
net::IpAddr,
|
||||||
@@ -16,115 +15,113 @@ use rtnetlink::packet_route::{
|
|||||||
|
|
||||||
use super::{NUDState, NeighborItem};
|
use super::{NUDState, NeighborItem};
|
||||||
|
|
||||||
// dont you love https://github.com/rust-netlink/rtnetlink/blob/main/examples/get_neighbours.rs
|
/// Fetch neighbors via rtnetlink. Empty slice = no filter (match all).
|
||||||
// NeighborItem.state guarantees to be a single thing.
|
/// Non-empty slice = match ANY in the set.
|
||||||
|
// https://github.com/rust-netlink/rtnetlink/blob/main/examples/get_neighbours.rs
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
ip: Option<IpAddr>,
|
ips: &[IpAddr],
|
||||||
dev: Option<&str>,
|
devs: &[impl AsRef<str>],
|
||||||
nud: &[NUDState],
|
nuds: &[NUDState],
|
||||||
|
macs: &[MacAddr],
|
||||||
) -> anyhow::Result<Vec<NeighborItem>> {
|
) -> anyhow::Result<Vec<NeighborItem>> {
|
||||||
let (gip, gdev, gnud) = (ip, dev, nud);
|
|
||||||
let (conn, handle, _) = rtnetlink::new_connection()?;
|
let (conn, handle, _) = rtnetlink::new_connection()?;
|
||||||
tokio::spawn(conn); // every time?
|
tokio::spawn(conn);
|
||||||
let mut neighbor_data = handle.neighbours().get().execute();
|
|
||||||
let nudset: HashSet<&NUDState> = HashSet::from_iter(gnud);
|
|
||||||
|
|
||||||
// map ifindex to name
|
let mut neighbor_data = handle.neighbours().get().execute();
|
||||||
let mut ball: HashMap<u32, String> = HashMap::new();
|
|
||||||
|
// Build filter sets (empty = match all)
|
||||||
|
let ip_set: HashSet<IpAddr> = ips.iter().copied().collect();
|
||||||
|
let dev_set: HashSet<&str> = devs.iter().map(AsRef::as_ref).collect();
|
||||||
|
let nud_set: HashSet<&NUDState> = nuds.iter().collect();
|
||||||
|
let mac_set: HashSet<MacAddr> = macs.iter().copied().collect();
|
||||||
|
|
||||||
|
// Cache ifindex -> name
|
||||||
|
let mut ifname_cache: HashMap<u32, String> = HashMap::new();
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
'big: while let Some(neighbour_message_item) = neighbor_data.try_next().await? {
|
|
||||||
// Filter by address family
|
'row: while let Some(msg) = neighbor_data.try_next().await? {
|
||||||
|
// Only IPv4/IPv6, skip NOARP
|
||||||
if !matches!(
|
if !matches!(
|
||||||
neighbour_message_item.header.family,
|
msg.header.family,
|
||||||
AddressFamily::Inet | AddressFamily::Inet6
|
AddressFamily::Inet | AddressFamily::Inet6
|
||||||
) || matches!(neighbour_message_item.header.state, NeighbourState::Noarp)
|
) || matches!(msg.header.state, NeighbourState::Noarp)
|
||||||
// copilot says this to match ip -j n s
|
|
||||||
{
|
{
|
||||||
continue 'big;
|
continue 'row;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = vec![
|
let state: NUDState = msg.header.state.try_into().unwrap_or_default();
|
||||||
neighbour_message_item
|
|
||||||
.header
|
|
||||||
.state
|
|
||||||
.try_into()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
]; // ONE ITEM. why tf ts design json.
|
|
||||||
let mut ip = None;
|
let mut ip = None;
|
||||||
let mut mac = None;
|
let mut mac = None;
|
||||||
|
|
||||||
for neigh_attr in neighbour_message_item.attributes {
|
for attr in msg.attributes {
|
||||||
match neigh_attr {
|
match attr {
|
||||||
NeighbourAttribute::Destination(neighbour_address) => match neighbour_address {
|
NeighbourAttribute::Destination(addr) => match addr {
|
||||||
NeighbourAddress::Inet(ipv4_addr) => ip = Some(ipv4_addr.into()),
|
NeighbourAddress::Inet(v4) => ip = Some(IpAddr::from(v4)),
|
||||||
NeighbourAddress::Inet6(ipv6_addr) => ip = Some(ipv6_addr.into()),
|
NeighbourAddress::Inet6(v6) => ip = Some(IpAddr::from(v6)),
|
||||||
_ => continue 'big,
|
_ => continue 'row,
|
||||||
},
|
},
|
||||||
NeighbourAttribute::LinkLocalAddress(items) => {
|
NeighbourAttribute::LinkLocalAddress(bytes) => {
|
||||||
mac = match items.len() {
|
mac = match bytes.len() {
|
||||||
6 => items.first_chunk::<6>().map(|&e| MacAddr::from(e)),
|
6 => bytes.first_chunk::<6>().map(|&b| MacAddr::from(b)),
|
||||||
8 => items.first_chunk::<8>().map(|&e| MacAddr::from(e)),
|
8 => bytes.first_chunk::<8>().map(|&b| MacAddr::from(b)),
|
||||||
_ => continue 'big,
|
_ => continue 'row,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => continue,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exquisite
|
// Resolve ifindex -> name (cached)
|
||||||
let dev = if let Some(cached) = ball.get(&neighbour_message_item.header.ifindex) {
|
let dev = match ifname_cache.get(&msg.header.ifindex) {
|
||||||
Some(cached.clone())
|
Some(name) => Some(name.clone()),
|
||||||
} else {
|
None => {
|
||||||
// Query and cache
|
|
||||||
let name = handle
|
let name = handle
|
||||||
.link()
|
.link()
|
||||||
.get()
|
.get()
|
||||||
.match_index(neighbour_message_item.header.ifindex)
|
.match_index(msg.header.ifindex)
|
||||||
.execute()
|
.execute()
|
||||||
.try_next()
|
.try_next()
|
||||||
.await?
|
.await?
|
||||||
.and_then(|a| {
|
.and_then(|link| {
|
||||||
a.attributes.into_iter().find_map(|attr| match attr {
|
link.attributes.into_iter().find_map(|a| match a {
|
||||||
LinkAttribute::IfName(name) => Some(name),
|
LinkAttribute::IfName(n) => Some(n),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(ref n) = name {
|
if let Some(ref n) = name {
|
||||||
ball.insert(neighbour_message_item.header.ifindex, n.clone());
|
ifname_cache.insert(msg.header.ifindex, n.clone());
|
||||||
}
|
}
|
||||||
name
|
name
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (Some(ip), Some(dev)) = (ip, dev) else {
|
let (Some(ip), Some(dev)) = (ip, dev) else {
|
||||||
continue 'big;
|
continue 'row;
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
// Apply filters (empty set = match all)
|
||||||
// low block
|
if !ip_set.is_empty() && !ip_set.contains(&ip) {
|
||||||
if let Some(fip) = gip
|
continue 'row;
|
||||||
&& fip != ip
|
|
||||||
{
|
|
||||||
continue 'big;
|
|
||||||
}
|
}
|
||||||
if let Some(fdev) = gdev
|
if !dev_set.is_empty() && !dev_set.contains(dev.as_str()) {
|
||||||
&& dev != fdev
|
continue 'row;
|
||||||
{
|
|
||||||
continue 'big;
|
|
||||||
}
|
}
|
||||||
if !nudset.is_empty() && !nudset.contains(&state[0]) {
|
if !nud_set.is_empty() && !nud_set.contains(&state) {
|
||||||
continue 'big;
|
continue 'row;
|
||||||
};
|
}
|
||||||
|
if !mac_set.is_empty() && !mac.is_some_and(|m| mac_set.contains(&m)) {
|
||||||
|
continue 'row;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(NeighborItem {
|
result.push(NeighborItem {
|
||||||
ip,
|
ip,
|
||||||
dev: Some(dev),
|
dev: Some(dev),
|
||||||
mac,
|
mac,
|
||||||
state,
|
state: vec![state],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(result) // now i need another pass to filter out the uh.
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<NeighbourState> for NUDState {
|
impl TryFrom<NeighbourState> for NUDState {
|
||||||
@@ -140,7 +137,7 @@ impl TryFrom<NeighbourState> for NUDState {
|
|||||||
NeighbourState::Permanent => Ok(Self::Permanent),
|
NeighbourState::Permanent => Ok(Self::Permanent),
|
||||||
NeighbourState::None => Ok(Self::None),
|
NeighbourState::None => Ok(Self::None),
|
||||||
NeighbourState::Other(e) => Ok(Self::Other(e)),
|
NeighbourState::Other(e) => Ok(Self::Other(e)),
|
||||||
_ => Err(u16::MAX), // idk
|
_ => Err(u16::MAX),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ use lda_ipjs::subcommands::{address, neighbor};
|
|||||||
|
|
||||||
#[tokio::test] // ← Use tokio::test instead of manual #[tokio::main]
|
#[tokio::test] // ← Use tokio::test instead of manual #[tokio::main]
|
||||||
async fn ball1() -> anyhow::Result<()> {
|
async fn ball1() -> anyhow::Result<()> {
|
||||||
let result = neighbor::nl::get(None, None, &[]).await?;
|
let result = neighbor::nl::get(&[], &[] as &[&str], &[], &[]).await?;
|
||||||
println!("netlink results: {:?}", result);
|
println!("netlink results: {:?}", result);
|
||||||
Ok(()) // ← Don't force error, let it succeed
|
Ok(()) // ← Don't force error, let it succeed
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ async fn ball_compare_backends() -> anyhow::Result<()> {
|
|||||||
println!("Got {} entries from JSON", json_result.len());
|
println!("Got {} entries from JSON", json_result.len());
|
||||||
|
|
||||||
println!("\n=== Netlink Backend ===");
|
println!("\n=== Netlink Backend ===");
|
||||||
let nl_result = neighbor::nl::get(None, None, &[]).await?;
|
let nl_result = neighbor::nl::get(&[], &[] as &[&str], &[], &[]).await?;
|
||||||
println!("Got {} entries from netlink", nl_result.len());
|
println!("Got {} entries from netlink", nl_result.len());
|
||||||
|
|
||||||
// Compare counts
|
// Compare counts
|
||||||
|
|||||||
+3
-19
@@ -6,25 +6,9 @@ use thiserror::Error;
|
|||||||
#[derive(Debug, Display, Error)]
|
#[derive(Debug, Display, Error)]
|
||||||
pub enum IPNeighParseError {
|
pub enum IPNeighParseError {
|
||||||
IpWhere, // i never seen a ip neigh where the first thing aint an ip
|
IpWhere, // i never seen a ip neigh where the first thing aint an ip
|
||||||
IpParseError(AddrParseError),
|
IpParseError(#[from] AddrParseError),
|
||||||
// DevWhere,
|
// DevWhere,
|
||||||
MacParseError(macaddr::ParseError),
|
MacParseError(#[from] macaddr::ParseError),
|
||||||
StateWhere, // i never seen a ip neigh without the big FAILED at the end
|
StateWhere, // i never seen a ip neigh without the big FAILED at the end
|
||||||
StateParseError(strum::ParseError),
|
StateParseError(#[from] strum::ParseError),
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AddrParseError> for IPNeighParseError {
|
|
||||||
fn from(value: AddrParseError) -> Self {
|
|
||||||
Self::IpParseError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<macaddr::ParseError> for IPNeighParseError {
|
|
||||||
fn from(value: macaddr::ParseError) -> Self {
|
|
||||||
Self::MacParseError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<strum::ParseError> for IPNeighParseError {
|
|
||||||
fn from(value: strum::ParseError) -> Self {
|
|
||||||
Self::StateParseError(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-126
@@ -1,8 +1,8 @@
|
|||||||
use lda_ipjs::subcommands::neighbor;
|
use lda_ipjs::subcommands::neighbor;
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
|
|
||||||
use crate::arpparse::{self, IpNeighLine, NUDState};
|
use crate::arpparse::{IpNeighLine, NUDState};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
@@ -13,20 +13,7 @@ pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>>
|
|||||||
.map(|c| c.ip()))
|
.map(|c| c.ip()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// good now
|
/// Query neighbor table with multi-filters. Empty slice = no filter.
|
||||||
//
|
|
||||||
// Current logic: When filtering by exactly 1 dev/mac, exclude entries missing that field.
|
|
||||||
// This is because missing dev/mac usually means the entry is incomplete/transient.
|
|
||||||
//
|
|
||||||
// when there is only one MACs (getmac got some), the result will not have them fields.
|
|
||||||
// so there are three cases:
|
|
||||||
//
|
|
||||||
// 1. dont got nothing: take all of them (macset.is_empty())
|
|
||||||
// 2. exactly one: pre-filtered by ip, everything matches,
|
|
||||||
// devset.len() != 1 returns false, but then it works????
|
|
||||||
// OH THIS fuckass code i added it in the get_mac
|
|
||||||
// 3. devset.len() > 1. if none then absolutely not match,
|
|
||||||
// if some then check with the set; thats normal
|
|
||||||
pub async fn get_macs(
|
pub async fn get_macs(
|
||||||
machine_names: &[impl AsRef<str>],
|
machine_names: &[impl AsRef<str>],
|
||||||
ips: &[IpAddr],
|
ips: &[IpAddr],
|
||||||
@@ -34,140 +21,64 @@ pub async fn get_macs(
|
|||||||
state: &[NUDState],
|
state: &[NUDState],
|
||||||
macs: &[MacAddr],
|
macs: &[MacAddr],
|
||||||
) -> Result<Vec<IpNeighLine>> {
|
) -> Result<Vec<IpNeighLine>> {
|
||||||
let mut ip_set: HashSet<IpAddr> = ips.iter().map(|ip| ip.to_canonical()).collect();
|
// Resolve machine names to IPs
|
||||||
let ip_m: HashSet<IpAddr> = if !machine_names.is_empty() {
|
let resolved_ips: HashSet<IpAddr> = if !machine_names.is_empty() {
|
||||||
futures::future::try_join_all(machine_names.iter().map(|c| get_ips(c.as_ref())))
|
futures::future::try_join_all(machine_names.iter().map(|n| get_ips(n.as_ref())))
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
HashSet::new()
|
||||||
};
|
|
||||||
let ip_all = if ip_set.is_empty() && ip_m.is_empty() {
|
|
||||||
None
|
|
||||||
} else if ip_set.is_empty() {
|
|
||||||
Some(ip_m)
|
|
||||||
} else if ip_m.is_empty() {
|
|
||||||
Some(ip_set)
|
|
||||||
} else {
|
|
||||||
Some({
|
|
||||||
ip_set.retain(|c| ip_m.contains(c)); // inline AHHH
|
|
||||||
ip_set
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt_dev = if devs.len() > 1 {
|
// Merge provided IPs with resolved IPs
|
||||||
None
|
let ip_filter: Vec<IpAddr> = if ips.is_empty() && resolved_ips.is_empty() {
|
||||||
|
vec![]
|
||||||
|
} else if ips.is_empty() {
|
||||||
|
resolved_ips.into_iter().collect()
|
||||||
|
} else if resolved_ips.is_empty() {
|
||||||
|
ips.iter().map(|ip| ip.to_canonical()).collect()
|
||||||
} else {
|
} else {
|
||||||
devs.iter().next().map(AsRef::as_ref)
|
// Intersection: only IPs that appear in both
|
||||||
};
|
ips.iter()
|
||||||
|
.map(|ip| ip.to_canonical())
|
||||||
let run_one = |to_ip: Option<IpAddr>| get_mac(to_ip, opt_dev, state);
|
.filter(|ip| resolved_ips.contains(ip))
|
||||||
|
|
||||||
let mut ip_filtered = if let Some(something) = ip_all {
|
|
||||||
if something.len() == 1 {
|
|
||||||
run_one(something.into_iter().next()).await?
|
|
||||||
} else {
|
|
||||||
run_one(None)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.filter(|c| something.contains(&c.ip))
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
|
||||||
} else {
|
|
||||||
run_one(None).await?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply additional filters if any were provided
|
// Convert state filter
|
||||||
if !devs.is_empty() || !macs.is_empty() {
|
let nud_filter: Vec<neighbor::NUDState> = state.iter().copied().map(Into::into).collect();
|
||||||
let devset: HashSet<_> = devs.iter().map(AsRef::as_ref).collect();
|
|
||||||
let macset: HashSet<_> = macs.iter().collect();
|
|
||||||
|
|
||||||
ip_filtered.retain(|entry| {
|
// Convert devs to &str for nl::get
|
||||||
// Dev filter: if we're filtering by dev, entry must have a dev AND it must be in the set
|
let dev_strs: Vec<&str> = devs.iter().map(AsRef::as_ref).collect();
|
||||||
let dev_ok =
|
|
||||||
devset.is_empty() || entry.dev.as_deref().is_some_and(|d| devset.contains(d));
|
|
||||||
|
|
||||||
// MAC filter: if we're filtering by MAC, entry must have a MAC AND it must be in the set
|
// Single rtnetlink call with all filters
|
||||||
let mac_ok = macset.is_empty() || entry.mac.is_some_and(|m| macset.contains(&m));
|
let results: Vec<IpNeighLine> = neighbor::nl::get(&ip_filter, &dev_strs, &nud_filter, macs)
|
||||||
|
.await
|
||||||
|
.context("rtnetlink failed")?
|
||||||
|
.into_iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect();
|
||||||
|
|
||||||
dev_ok && mac_ok
|
Ok(results)
|
||||||
})
|
|
||||||
};
|
|
||||||
Ok(ip_filtered)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// the atomic get_macs. handle ONE thing only.
|
/// Legacy single-filter wrapper. Use get_macs for multi-filter.
|
||||||
// // this one sucks shit
|
#[allow(dead_code)]
|
||||||
// pub async fn get_mac(
|
|
||||||
// ip: Option<IpAddr>,
|
|
||||||
// dev: Option<&str>,
|
|
||||||
// state: &[NUDState],
|
|
||||||
// ) -> Result<Vec<IpNeighLine>> {
|
|
||||||
// use lda_ipjs::subcommands::neighbor as ipjs_neigh;
|
|
||||||
// let ipjs_states: Vec<ipjs_neigh::NUDState> = state.iter().copied().map(Into::into).collect();
|
|
||||||
|
|
||||||
// let items = ipjs_neigh::json::get(ip, dev, &ipjs_states)
|
|
||||||
// .await
|
|
||||||
// .context("Calling ip -j neigh failed")?;
|
|
||||||
|
|
||||||
// let lines = items.into_iter().map(Into::into).collect();
|
|
||||||
|
|
||||||
// Ok(lines)
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// the atomic get_macs. handle ONE thing only.
|
|
||||||
// 17 - 25 ms full
|
|
||||||
pub async fn _get_mac(
|
|
||||||
ip: Option<IpAddr>,
|
|
||||||
dev: Option<&str>,
|
|
||||||
state: &[NUDState],
|
|
||||||
) -> Result<Vec<IpNeighLine>> {
|
|
||||||
let mut args: Vec<String> = vec!["neigh".into(), "show".into()];
|
|
||||||
if let Some(ip) = ip {
|
|
||||||
args.push("to".into());
|
|
||||||
args.push(ip.to_string());
|
|
||||||
}
|
|
||||||
if let Some(d) = dev {
|
|
||||||
args.push("dev".into());
|
|
||||||
args.push(d.to_string());
|
|
||||||
}
|
|
||||||
for nud in state {
|
|
||||||
args.push("nud".into());
|
|
||||||
args.push(nud.as_ip_neigh_arg().into());
|
|
||||||
}
|
|
||||||
let cmd = "ip";
|
|
||||||
let mut u = tokio::process::Command::new(cmd);
|
|
||||||
u.args(args);
|
|
||||||
let out = u.output().await?;
|
|
||||||
|
|
||||||
if !out.status.success() {
|
|
||||||
bail!(String::from_utf8_lossy(&out.stderr).into_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let lines = String::from_utf8_lossy(&out.stdout);
|
|
||||||
let parsed = lines.lines().flat_map(arpparse::parse_ip_neigh_line);
|
|
||||||
let rows: Vec<IpNeighLine> = if let Some(d) = dev {
|
|
||||||
parsed.map(IpNeighLine::_with_dev(d)).collect()
|
|
||||||
} else {
|
|
||||||
parsed.collect()
|
|
||||||
};
|
|
||||||
Ok(rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15 - 20 ms full
|
|
||||||
pub async fn get_mac(
|
pub async fn get_mac(
|
||||||
ip: Option<IpAddr>,
|
ip: Option<IpAddr>,
|
||||||
dev: Option<&str>,
|
dev: Option<&str>,
|
||||||
state: &[NUDState],
|
state: &[NUDState],
|
||||||
) -> Result<Vec<IpNeighLine>> {
|
) -> Result<Vec<IpNeighLine>> {
|
||||||
let state2: Vec<neighbor::NUDState> = state.iter().copied().map(Into::into).collect();
|
let ips: Vec<IpAddr> = ip.into_iter().collect();
|
||||||
Ok(neighbor::nl::get(ip, dev, &state2)
|
let devs: Vec<&str> = dev.into_iter().collect();
|
||||||
|
let nud: Vec<neighbor::NUDState> = state.iter().copied().map(Into::into).collect();
|
||||||
|
|
||||||
|
Ok(neighbor::nl::get(&ips, &devs, &nud, &[])
|
||||||
.await
|
.await
|
||||||
.context("rtnetlink failed")?
|
.context("rtnetlink failed")?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect())
|
.collect())
|
||||||
// how did i just do that
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user