Compare commits
10
Commits
v0.1.5
...
a534c58262
No files matched your search
File renamed without changes.
Generated
+11
@@ -610,6 +610,17 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lda-ipjs"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"macaddr",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with",
|
||||||
|
"strum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.175"
|
version = "0.2.175"
|
||||||
|
|||||||
@@ -26,3 +26,6 @@ strip = true
|
|||||||
[features]
|
[features]
|
||||||
default = ["very-smart-parsing"]
|
default = ["very-smart-parsing"]
|
||||||
very-smart-parsing = [] # this is the a-bit-redundant parse thing that copilot made
|
very-smart-parsing = [] # this is the a-bit-redundant parse thing that copilot made
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["ipjs"]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "lda-ipjs"
|
||||||
|
description = "ip -j show schemas"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
macaddr = { version = "1.0.1", features = ["serde", "serde_std"] }
|
||||||
|
strum = { version = "0.27.2", features = ["derive", "strum_macros"] }
|
||||||
|
serde_json = "1.0.143"
|
||||||
|
serde_with = { version = "3.14.0", features = ["json"] }
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//! low
|
||||||
|
//!
|
||||||
|
//! # lda-ipj
|
||||||
|
//!
|
||||||
|
//! this package will represent all my needs with the all the subcommands of ip -j.
|
||||||
|
//!
|
||||||
|
//! ## what i need
|
||||||
|
//!
|
||||||
|
//! ```console
|
||||||
|
//! ip -j neigh
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! i also need to see devices and idk MAYBE maybe not MAYBE UHHHHHH maybe broadcast
|
||||||
|
//!
|
||||||
|
|
||||||
|
pub mod neighbor;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
// i want a generalized way to build and call
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
//! ```
|
||||||
|
//! ip -j n s
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! yes. this is a real call.
|
||||||
|
|
||||||
|
use crate::utils::serialize::mac::{des_opm, ser_opm};
|
||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use macaddr::MacAddr;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use strum::{Display, EnumString};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct NeighborInput {
|
||||||
|
/// supports only the last item (ignore), `to` keyword is optional
|
||||||
|
to: IpAddr,
|
||||||
|
/// supports only one item (it complains)
|
||||||
|
dev: String, // im all for simplicity
|
||||||
|
/// takes multiple, has to have `nud` before bro or it will think you `to`
|
||||||
|
nud: Vec<NUDState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// as input this must be lowercase. as output it is uppercase
|
||||||
|
#[derive(Debug, PartialEq, Eq, EnumString, Display, Clone, Copy, Hash, Serialize, Deserialize)]
|
||||||
|
#[strum(serialize_all = "lowercase", ascii_case_insensitive)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
pub enum NUDState {
|
||||||
|
/// the neighbour entry is valid forever and can
|
||||||
|
/// be only be removed administratively.
|
||||||
|
Permanent,
|
||||||
|
/// the neighbour entry is valid. No attempts to
|
||||||
|
/// validate this entry will be made but it can
|
||||||
|
/// be removed when its lifetime expires.
|
||||||
|
Noarp,
|
||||||
|
/// the neighbour entry is valid until the
|
||||||
|
/// reachability timeout expires.
|
||||||
|
Reachable,
|
||||||
|
/// the neighbour entry is valid but suspicious.
|
||||||
|
/// This option to ip neigh does not change the
|
||||||
|
/// neighbour state if it was valid and the
|
||||||
|
/// address is not changed by this command.
|
||||||
|
Stale,
|
||||||
|
/// this is a pseudo state used when initially
|
||||||
|
/// creating a neighbour entry or after trying to
|
||||||
|
/// remove it before it becomes free to do so.
|
||||||
|
None,
|
||||||
|
/// the neighbour entry has not (yet) been
|
||||||
|
/// validated/resolved.
|
||||||
|
Incomplete,
|
||||||
|
/// neighbor entry validation is currently
|
||||||
|
/// delayed.
|
||||||
|
Delay,
|
||||||
|
/// neighbor is being probed.
|
||||||
|
Probe,
|
||||||
|
/// max number of probes exceeded without
|
||||||
|
/// success, neighbor validation has ultimately
|
||||||
|
/// failed.
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||||
|
|
||||||
|
pub struct NeighborItem {
|
||||||
|
dst: IpAddr,
|
||||||
|
dev: String,
|
||||||
|
#[serde(deserialize_with = "des_opm", serialize_with = "ser_opm")]
|
||||||
|
lladdr: Option<MacAddr>,
|
||||||
|
state: Vec<NUDState>,
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod serialize;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
use macaddr::MacAddr;
|
||||||
|
use serde::{self, Deserialize, Deserializer, de::Error as DeError};
|
||||||
|
use serde::{Serialize, Serializer, de};
|
||||||
|
|
||||||
|
pub fn serialize_macs<S>(macs: &[MacAddr], serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let strings: Vec<String> = macs.iter().map(|m| m.to_string()).collect();
|
||||||
|
serde::Serialize::serialize(&strings, serializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a MacAddr as a string
|
||||||
|
pub fn serialize_mac<S>(mac: &MacAddr, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&mac.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize a MacAddr from a string
|
||||||
|
pub fn deserialize_mac<'de, D>(deserializer: D) -> Result<MacAddr, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
|
||||||
|
s.parse::<MacAddr>().map_err(DeError::custom)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// serialize an [`Option<MacAddr>`]
|
||||||
|
pub fn ser_opm<S: Serializer>(bro: &Option<MacAddr>, ser: S) -> Result<S::Ok, S::Error> {
|
||||||
|
Option::<String>::serialize(&bro.as_ref().map(ToString::to_string), ser)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// deserialize an [`Option<MacAddr>`]
|
||||||
|
pub fn des_opm<'de, D>(des: D) -> Result<Option<MacAddr>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Option::<&str>::deserialize(des)?
|
||||||
|
.map(str::parse)
|
||||||
|
.transpose()
|
||||||
|
.map_err(de::Error::custom)
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod mac;
|
||||||
|
pub mod vec;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::de;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum OneOrMany<T> {
|
||||||
|
One(T),
|
||||||
|
Many(Vec<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vec_from_strs<'de, D, T>(des: D) -> Result<Vec<T>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
T: std::str::FromStr,
|
||||||
|
T::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
let raw: OneOrMany<String> = OneOrMany::<String>::deserialize(des)?;
|
||||||
|
let mut out = Vec::new();
|
||||||
|
match raw {
|
||||||
|
OneOrMany::One(s) => {
|
||||||
|
let t = s.trim();
|
||||||
|
if !t.is_empty() {
|
||||||
|
out.push(t.parse().map_err(de::Error::custom)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OneOrMany::Many(vs) => {
|
||||||
|
for s in vs {
|
||||||
|
let t = s.trim();
|
||||||
|
if t.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push(t.parse().map_err(de::Error::custom)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# taking back my code AHHHH
|
||||||
|
|
||||||
|
this is going to be lengthy. ci is spotless but idk how it works cuz powershell. this is good but i cant afford VPN on the router; we will need to do some bullshit.
|
||||||
|
|
||||||
|
## overview
|
||||||
|
|
||||||
|
we need a:
|
||||||
|
|
||||||
|
- small ahh bin in the router.
|
||||||
|
- forwards a shell and some functions.
|
||||||
|
- may be shell calling fns maybe some unsafe c. i cant unsafe c.
|
||||||
|
- big ahh bin in the vps.
|
||||||
|
- connect to the small ahh bin thru the wide ass internet.
|
||||||
|
- forwards some APIs.
|
||||||
|
- static site
|
||||||
|
- now need no hosts; apis can be called anywhere anyway.
|
||||||
|
- do offer these in the big ahh bins tho lets not miss out some shit.
|
||||||
|
- automation
|
||||||
|
- gitea
|
||||||
|
- on the vps or the uh idk.
|
||||||
|
- ABSOLUTELY can auto build main:latest every day / other day.
|
||||||
|
- and releases.
|
||||||
|
- router
|
||||||
|
- a script to pull the tar. from actions artifacts or from releases. (lua or shell yo)
|
||||||
|
- a script for the bin and the init.d scripts for these two.
|
||||||
|
- vps
|
||||||
|
- a systemd service (ima use ai for ts ngl)
|
||||||
|
- an auto updater. maybe we overkill and dnf. maybe go easy and git clone + cargo install. maybe we pull from release or nightly. idk
|
||||||
|
|
||||||
|
i think idk
|
||||||
|
|
||||||
|
## router
|
||||||
|
|
||||||
|
### current
|
||||||
|
|
||||||
|
everything.
|
||||||
|
|
||||||
|
we have some functions that call ip neigh a bajillion times.
|
||||||
|
we have some other functions that read the same file/folder in /sys or sum shit.
|
||||||
|
we have a function that broadcast to a set ahh address.
|
||||||
|
|
||||||
|
we have some routes; and some data structures acting as schema for them ahh. we dont do posts with json but gets with query strings for some ahh reasons.
|
||||||
|
|
||||||
|
these are carryover from the early days.
|
||||||
|
|
||||||
|
also from the early days are the js.
|
||||||
|
|
||||||
|
static was 100% ai with 0 intervention.
|
||||||
|
|
||||||
|
we have a search bar of sort that hooks through a smart endpoint that searches for everything.
|
||||||
|
|
||||||
|
we have some bullshit. and two buttons to check and wake. that pings `/api/smart/{query}` and `/wake?name=query`. why.
|
||||||
|
we have the result of the wake and or status and the errors in the first text box.
|
||||||
|
|
||||||
|
the list of cliets with names connected to the router the second box.
|
||||||
|
thats cool.
|
||||||
|
|
||||||
|
### changes?
|
||||||
|
|
||||||
|
idk changes.
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
use macaddr::MacAddr6;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{net::IpAddr, process::Command};
|
||||||
|
|
||||||
|
// Raw JSON shape from ip -j -4 address show
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct IpJAddrInfo {
|
||||||
|
family: Option<String>,
|
||||||
|
local: Option<String>,
|
||||||
|
broadcast: Option<String>,
|
||||||
|
scope: Option<String>,
|
||||||
|
label: Option<String>,
|
||||||
|
prefixlen: Option<u8>,
|
||||||
|
// many more exist; we only take what we need
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct IpJAddrEntry {
|
||||||
|
ifname: String,
|
||||||
|
// interface MAC address (present on non-loopback):
|
||||||
|
address: Option<String>,
|
||||||
|
addr_info: Option<Vec<IpJAddrInfo>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AddrInfo {
|
||||||
|
pub local: IpAddr,
|
||||||
|
pub broadcast: Option<IpAddr>,
|
||||||
|
pub scope: Option<String>,
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub prefixlen: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct IpAEntry {
|
||||||
|
pub ifname: String,
|
||||||
|
pub mac: Option<MacAddr6>,
|
||||||
|
pub addr_info: Vec<AddrInfo>,
|
||||||
|
// IPv4 only (inet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run ip -j -4 address show and deserialize
|
||||||
|
fn read_ip_json() -> Result<Vec<IpJAddrEntry>, Box<dyn std::error::Error>> {
|
||||||
|
let out = Command::new("ip")
|
||||||
|
.args(["-j", "-4", "address", "show"])
|
||||||
|
.output()?;
|
||||||
|
if !out.status.success() {
|
||||||
|
return Err(format!("ip exited with {}", out.status).into());
|
||||||
|
}
|
||||||
|
let entries: Vec<IpJAddrEntry> = serde_json::from_slice(&out.stdout)?;
|
||||||
|
Ok(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ip(s: &str) -> Option<IpAddr> {
|
||||||
|
s.parse::<IpAddr>().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_mac(s: &str) -> Option<MacAddr6> {
|
||||||
|
// ip outputs lowercase aa:bb:..., which MacAddr6 can parse
|
||||||
|
s.parse::<MacAddr6>().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public: read and convert to a cleaned model
|
||||||
|
pub fn get_ip_addr_entries() -> Result<Vec<IpAEntry>, Box<dyn std::error::Error>> {
|
||||||
|
let raw = read_ip_json()?;
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for e in raw {
|
||||||
|
let mac = e.address.as_deref().and_then(parse_mac);
|
||||||
|
let mut infos = Vec::new();
|
||||||
|
|
||||||
|
if let Some(list) = e.addr_info {
|
||||||
|
for ai in list {
|
||||||
|
// keep only IPv4
|
||||||
|
if ai.family.as_deref() != Some("inet") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(local) = ai.local.as_deref().and_then(parse_ip) {
|
||||||
|
let broadcast = ai.broadcast.as_deref().and_then(parse_ip);
|
||||||
|
infos.push(AddrInfo {
|
||||||
|
local,
|
||||||
|
broadcast,
|
||||||
|
scope: ai.scope,
|
||||||
|
label: ai.label,
|
||||||
|
prefixlen: ai.prefixlen,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(IpAEntry {
|
||||||
|
ifname: e.ifname,
|
||||||
|
mac,
|
||||||
|
addr_info: infos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: first “good” (global) broadcast on an interface
|
||||||
|
pub fn broadcast_for_ifname(ifname: &str) -> Result<Option<IpAddr>, Box<dyn std::error::Error>> {
|
||||||
|
let entries = get_ip_addr_entries()?;
|
||||||
|
let dev = entries.into_iter().find(|e| e.ifname == ifname);
|
||||||
|
if let Some(dev) = dev {
|
||||||
|
// Prefer scope=global with a broadcast; else any broadcast
|
||||||
|
if let Some(b) = dev
|
||||||
|
.addr_info
|
||||||
|
.iter()
|
||||||
|
.filter(|ai| ai.scope.as_deref() == Some("global"))
|
||||||
|
.filter_map(|ai| ai.broadcast)
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
return Ok(Some(b));
|
||||||
|
}
|
||||||
|
if let Some(b) = dev.addr_info.iter().filter_map(|ai| ai.broadcast).next() {
|
||||||
|
return Ok(Some(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: broadcast for a given interface MAC
|
||||||
|
pub fn broadcast_for_mac(
|
||||||
|
mac: MacAddr6,
|
||||||
|
) -> Result<Option<(String, IpAddr)>, Box<dyn std::error::Error>> {
|
||||||
|
let entries = get_ip_addr_entries()?;
|
||||||
|
for e in entries {
|
||||||
|
if let Some(m) = e.mac {
|
||||||
|
if m == mac {
|
||||||
|
// prefer global broadcast
|
||||||
|
if let Some(b) = e
|
||||||
|
.addr_info
|
||||||
|
.iter()
|
||||||
|
.filter(|ai| ai.scope.as_deref() == Some("global"))
|
||||||
|
.filter_map(|ai| ai.broadcast)
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
return Ok(Some((e.ifname, b)));
|
||||||
|
}
|
||||||
|
if let Some(b) = e.addr_info.iter().filter_map(|ai| ai.broadcast).next() {
|
||||||
|
return Ok(Some((e.ifname, b)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: if nothing else, use limited broadcast (may be filtered on some networks)
|
||||||
|
pub fn fallback_broadcast() -> IpAddr {
|
||||||
|
// 255.255.255.255
|
||||||
|
IpAddr::from([255, 255, 255, 255])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience: all broadcasts grouped by interface (IPv4)
|
||||||
|
pub fn all_broadcasts() -> Result<Vec<(String, IpAddr)>, Box<dyn std::error::Error>> {
|
||||||
|
let Some(r) = Some(12) else {
|
||||||
|
e.addr_info
|
||||||
|
.iter()
|
||||||
|
.filter(|ai| ai.scope.as_deref() == Some("global"))
|
||||||
|
.filter_map(|ai| ai.broadcast)
|
||||||
|
};
|
||||||
|
let entries = get_ip_addr_entries()?;
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for e in entries {
|
||||||
|
for ai in e.addr_info {
|
||||||
|
if let Some(b) = ai.broadcast {
|
||||||
|
out.push((e.ifname.clone(), b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
+1
-1
@@ -19,7 +19,7 @@ pub async fn _ping_ip_2<T: ToSocketAddrs>(_addr: T) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn _ping_ip_3<T: Into<IpAddr>>(addr: T) -> u8 {
|
pub async fn _ping_ip_3<T: Into<IpAddr>>(addr: T) -> u8 {
|
||||||
match get_mac(Some(addr.into()), None, None).await {
|
match get_mac(Some(addr.into()), None, &[] as &[NUDState]).await {
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
Ok(l) => l
|
Ok(l) => l
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
+4
-13
@@ -72,13 +72,8 @@ pub async fn get_macs(
|
|||||||
} else {
|
} else {
|
||||||
devs.iter().next().map(AsRef::as_ref)
|
devs.iter().next().map(AsRef::as_ref)
|
||||||
};
|
};
|
||||||
let opt_state = if state.len() > 1 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
state.iter().next().copied()
|
|
||||||
};
|
|
||||||
|
|
||||||
let run_one = |to_ip: Option<IpAddr>| get_mac(to_ip, opt_dev, opt_state);
|
let run_one = |to_ip: Option<IpAddr>| get_mac(to_ip, opt_dev, state);
|
||||||
|
|
||||||
let mut ip_filtered = if let Some(something) = ip_all {
|
let mut ip_filtered = if let Some(something) = ip_all {
|
||||||
if something.len() == 1 {
|
if something.len() == 1 {
|
||||||
@@ -97,7 +92,6 @@ pub async fn get_macs(
|
|||||||
// Apply additional filters if any were provided
|
// Apply additional filters if any were provided
|
||||||
if !devs.is_empty() || !macs.is_empty() || !state.is_empty() {
|
if !devs.is_empty() || !macs.is_empty() || !state.is_empty() {
|
||||||
let devset: HashSet<_> = devs.iter().map(AsRef::as_ref).collect();
|
let devset: HashSet<_> = devs.iter().map(AsRef::as_ref).collect();
|
||||||
let nudset: HashSet<_> = state.iter().collect();
|
|
||||||
let macset: HashSet<_> = macs.iter().collect();
|
let macset: HashSet<_> = macs.iter().collect();
|
||||||
|
|
||||||
ip_filtered.retain(|entry| {
|
ip_filtered.retain(|entry| {
|
||||||
@@ -105,13 +99,10 @@ pub async fn get_macs(
|
|||||||
let dev_ok =
|
let dev_ok =
|
||||||
devset.is_empty() || entry.dev.as_deref().is_some_and(|d| devset.contains(d));
|
devset.is_empty() || entry.dev.as_deref().is_some_and(|d| devset.contains(d));
|
||||||
|
|
||||||
// NUD filter: always present, simple check
|
|
||||||
let nud_ok = nudset.is_empty() || nudset.contains(&entry.state);
|
|
||||||
|
|
||||||
// MAC filter: if we're filtering by MAC, entry must have a MAC AND it must be in the set
|
// MAC filter: if we're filtering by MAC, entry must have a MAC AND it must be in the set
|
||||||
let mac_ok = macset.is_empty() || entry.mac.is_some_and(|m| macset.contains(&m));
|
let mac_ok = macset.is_empty() || entry.mac.is_some_and(|m| macset.contains(&m));
|
||||||
|
|
||||||
dev_ok && nud_ok && mac_ok
|
dev_ok && mac_ok
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
Ok(ip_filtered)
|
Ok(ip_filtered)
|
||||||
@@ -121,7 +112,7 @@ pub async fn get_macs(
|
|||||||
pub async fn get_mac(
|
pub async fn get_mac(
|
||||||
ip: Option<IpAddr>,
|
ip: Option<IpAddr>,
|
||||||
dev: Option<&str>,
|
dev: Option<&str>,
|
||||||
state: Option<NUDState>,
|
state: &[NUDState],
|
||||||
) -> Result<Vec<IpNeighLine>> {
|
) -> Result<Vec<IpNeighLine>> {
|
||||||
let mut args: Vec<String> = vec!["neigh".into(), "show".into()];
|
let mut args: Vec<String> = vec!["neigh".into(), "show".into()];
|
||||||
if let Some(ip) = ip {
|
if let Some(ip) = ip {
|
||||||
@@ -132,7 +123,7 @@ pub async fn get_mac(
|
|||||||
args.push("dev".into());
|
args.push("dev".into());
|
||||||
args.push(d.to_string());
|
args.push(d.to_string());
|
||||||
}
|
}
|
||||||
if let Some(nud) = state {
|
for nud in state {
|
||||||
args.push("nud".into());
|
args.push("nud".into());
|
||||||
args.push(nud.as_ip_neigh_arg().into());
|
args.push(nud.as_ip_neigh_arg().into());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user