Restructured modules, integrated async-arp

This commit is contained in:
rzuasti
2026-01-13 11:01:53 -05:00
parent c4984c4ed2
commit 0a1e8d6171
6 changed files with 103 additions and 13 deletions
Generated
+11
View File
@@ -178,6 +178,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "ipnetwork"
version = "0.20.0"
@@ -226,7 +232,12 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
name = "oott"
version = "0.1.0"
dependencies = [
"afpacket",
"async-arp",
"futures",
"ipnet",
"pnet",
"tokio",
]
[[package]]
+5
View File
@@ -5,3 +5,8 @@ edition = "2024"
[dependencies]
async-arp = "0.4.0"
afpacket = {version = "0.2.3", features = ["async-tokio"]}
ipnet = "2.11.0"
pnet = "0.35.0"
tokio = {version = "1.43.0", features = ["io-util", "time", "rt", "macros", "sync"]}
futures = "0.3.31"
+3 -13
View File
@@ -1,19 +1,9 @@
use crate::device_finders::Device;
use async_arp::{Client, ClientConfigBuilder, ClientSpinner, ProbeStatus, Result};
use std::io::Write;
use std::time::{Duration, Instant};
mod helper_async_arp;
pub fn find() -> Vec<Device> {
let mut devices = Vec::new();
devices.push(Device {
mac_address: String::from("mac1"),
});
devices.push(Device {
mac_address: String::from("mac2"),
});
devices.push(Device {
mac_address: String::from("mac3"),
});
let devices = helper_async_arp::execute_probe(&String::from("eno1"));
devices
}
@@ -0,0 +1,39 @@
use async_arp::{Client, ClientConfigBuilder, ProbeStatus};
use std::time::{Duration, Instant};
use crate::device_finders::Device;
mod common;
#[tokio::main(flavor = "current_thread")]
pub async fn execute_probe(iface: &str) -> Vec<Device> {
let interface = common::interface_from(&iface);
let net = common::net_from(&interface).unwrap();
let client = Client::new(
ClientConfigBuilder::new(&iface)
.with_response_timeout(Duration::from_millis(500))
.build(),
)
.unwrap();
let inputs = common::generate_probe_inputs(net, interface);
let start = Instant::now();
let futures = inputs.into_iter().map(|input| client.probe(input));
let outcomes = futures::future::join_all(futures).await;
let scan_duration = start.elapsed();
let occupied = outcomes
.into_iter()
.filter_map(|outcome| outcome.ok())
.filter(|outcome| outcome.status == ProbeStatus::Occupied);
let mut devices = Vec::new();
for outcome in occupied {
devices.push(Device {
mac_address: outcome.target_ip.to_string(),
});
}
devices
}
@@ -0,0 +1,45 @@
use async_arp::{ProbeInput, ProbeInputBuilder};
use ipnet::Ipv4Net;
use pnet::datalink::{self, NetworkInterface};
use std::net::IpAddr;
// Generate the probe inputs for a scan
pub(crate) fn generate_probe_inputs(net: Ipv4Net, interface: NetworkInterface) -> Vec<ProbeInput> {
net.hosts()
.map(|target_ip| {
let sender_mac = interface
.mac
.ok_or("interface does not have mac address")
.unwrap();
ProbeInputBuilder::new()
.with_sender_mac(sender_mac)
.with_target_ip(target_ip)
.build()
.unwrap()
})
.collect()
}
// Find the desired network interface
pub(crate) fn interface_from(interface_name: &str) -> NetworkInterface {
datalink::interfaces()
.into_iter()
.find(|iface| iface.name == interface_name)
.ok_or_else(|| format!("interface {} not found", interface_name))
.unwrap()
}
// Find the network from which we are probing
pub(crate) fn net_from(interface: &NetworkInterface) -> Option<Ipv4Net> {
let net = interface
.ips
.iter()
.filter(|net| net.is_ipv4())
.take(1)
.next()?;
if let IpAddr::V4(ipv4) = net.ip() {
Some(Ipv4Net::new(ipv4, net.prefix()).unwrap())
} else {
None
}
}