From dda430556b7ebd614aba5aee71613f75e860811a Mon Sep 17 00:00:00 2001 From: rzuasti Date: Wed, 14 Jan 2026 09:53:01 -0500 Subject: [PATCH] Timeouts working, environment based config. Still have to figure out running sender and receiver in parallel --- .cargo/config.toml | 4 + Cargo.lock | 144 ++++++++++++++++++ Cargo.toml | 2 + src/config.rs | 19 +++ src/device_finders/arp.rs | 106 +++++++------ src/device_finders/arp/packet_send_receive.rs | 19 ++- src/main.rs | 11 +- 7 files changed, 242 insertions(+), 63 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 src/config.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..9c1a223 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[env] +RUST_LOG="info" +OOTT_ARP_SENDER_TIMEOUT="180" +OOTT_ARP_RECEIVER_TIMEOUT="300" diff --git a/Cargo.lock b/Cargo.lock index d0a015a..adb7513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,56 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "bitflags" version = "2.10.0" @@ -29,6 +79,35 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "errno" version = "0.3.14" @@ -54,6 +133,36 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jiff" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "libc" version = "0.2.180" @@ -69,6 +178,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + [[package]] name = "memchr" version = "2.7.6" @@ -92,10 +207,18 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "oott" version = "0.1.0" dependencies = [ + "env_logger", + "log", "pnet", "tokio", ] @@ -220,6 +343,21 @@ dependencies = [ "pnet_sys", ] +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "proc-macro2" version = "1.0.105" @@ -382,6 +520,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 5dede73..d844837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +env_logger = "0.11.8" +log = "0.4.29" pnet = "0.35.0" tokio = { version = "1.0", features = ["full"] } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b179730 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,19 @@ +use log::error; +use std::env; +use std::fmt::Display; +use std::str::FromStr; + +pub fn parse_env(variable: &str) -> Option +where + T: FromStr, + ::Err: Display, +{ + env::var(variable) + .map_err(|error| error!("{error}: {variable}")) + .ok() + .and_then(|raw| { + raw.parse::() + .map_err(|error| error!("{error}: {raw}")) + .ok() + }) +} diff --git a/src/device_finders/arp.rs b/src/device_finders/arp.rs index b682db1..ad2b218 100644 --- a/src/device_finders/arp.rs +++ b/src/device_finders/arp.rs @@ -1,17 +1,20 @@ -use crate::device_finders::{ - Device, - arp::packet_send_receive::{listen_for_packets, send_packet}, -}; +mod packet_send_receive; + +use crate::{config, device_finders::Device}; +use log::{debug, info, warn}; +use packet_send_receive::{listen_for_packets, send_packet}; use pnet::{ datalink::{self, Channel, NetworkInterface}, ipnetwork::IpNetwork, }; -use tokio::task; -use tokio::time::{Duration, Instant, sleep, timeout}; +use tokio::time::{Duration, Instant, timeout}; -mod packet_send_receive; +const DEFAULT_SENDER_TIMEOUT: u64 = 60; // 1 minute to send all packets - good for a class C network +const DEFAULT_RECEIVER_TIMEOUT: u64 = 300; // 5 minutes to wait per round to receive responses pub async fn find(interface: &str) -> Vec { + debug!("Looking up devices via ARP using interface {}", interface); + // Get the network device to use let network_interface: NetworkInterface = datalink::interfaces() .iter() @@ -40,11 +43,11 @@ pub async fn find(interface: &str) -> Vec { } }; - println!("Local MAC: {}", mac); - println!("Local IP: {}", ipv4_net); + info!("Local MAC: {}", mac); + info!("Local IP: {}", ipv4_net); // Data channel - println!("Creating data channel"); + debug!("Creating data channel"); let tunnel = datalink::channel(&network_interface, datalink::Config::default()) .expect("Failed to create datalink channel"); let (sender, receiver) = match tunnel { @@ -54,51 +57,50 @@ pub async fn find(interface: &str) -> Vec { let send_interface = network_interface.clone(); - println!("Starting sender"); - let start = Instant::now(); - - let send_result = timeout( - Duration::from_millis(10), + // Spawn sender thread + info!("Starting ARP sender"); + let sender_start = Instant::now(); + let sender_timeout = + config::parse_env("OOTT_ARP_SENDER_TIMEOUT").unwrap_or(DEFAULT_SENDER_TIMEOUT); + debug!("Sender timeout set to {} seconds", sender_timeout); + let sender_thread = timeout( + Duration::from_secs(sender_timeout), send_packet(sender, send_interface, ipv4_net, mac), - ) - .await; - match send_result { - Ok(_) => println!("Sender done"), - Err(_) => println!("Sender timed out"), - }; - println!( - "Sender took {} millisecs", - (Instant::now() - start).as_millis() ); - // let receive_result = timeout( - // Duration::from_secs(5), - // listen_for_packets(receiver, ipv4_net), - // ) - // .await; - // match receive_result { - // Ok(_) => println!("Receiver done"), - // Err(_) => println!("Receiver timed out"), - // }; + // Spawn receiver thread + info!("Starting ARP receiver"); + let receiver_start = Instant::now(); + let receiver_timeout = + config::parse_env("OOTT_ARP_RECEIVER_TIMEOUT").unwrap_or(DEFAULT_RECEIVER_TIMEOUT); + debug!("Receiver timeout set to {} seconds", receiver_timeout); + let receiver_thread = timeout( + Duration::from_secs(receiver_timeout), + listen_for_packets(receiver, ipv4_net), + ); - // println!("Spawning receiver threads"); - // let reciever_thread = thread::spawn(move || { - // listen_for_packets(receiver, ipv4_net); - // }); + // Now we wait + // Joining sender thread - It should have a shorter timeout than the receiver thread + let sender_result = sender_thread.await; + match sender_result { + Ok(_) => info!("ARP sender done"), + Err(_) => warn!("ARP sender timed out"), + }; + info!( + "ARP sender took {} secs", + (Instant::now() - sender_start).as_secs() + ); - // println!("Spawning sender thread"); - // let sender_thread = thread::spawn(move || { - // send_packet(sender, send_interface, ipv4_net, mac); - // }); - - // match sender_thread.join() { - // Ok(it) => println!("Sender thread done"), - // Err(err) => panic!("Error sending ARP packets"), - // }; - - // println!("Waiting to receive"); - // thread::sleep(Duration::from_secs(10)); - // println!("Done!"); + // Joining receiver thread + let receiver_result = receiver_thread.await; + match receiver_result { + Ok(_) => info!("ARP receiver done"), + Err(_) => warn!("ARP receiver timed out"), + }; + info!( + "ARP receiver took {} secs", + (Instant::now() - receiver_start).as_secs() + ); let mut devices = Vec::new(); devices.push(Device { @@ -107,7 +109,3 @@ pub async fn find(interface: &str) -> Vec { }); devices } - -async fn test_timeout() { - sleep(Duration::from_secs(10)).await; -} diff --git a/src/device_finders/arp/packet_send_receive.rs b/src/device_finders/arp/packet_send_receive.rs index 11b26b3..15b5165 100644 --- a/src/device_finders/arp/packet_send_receive.rs +++ b/src/device_finders/arp/packet_send_receive.rs @@ -1,3 +1,4 @@ +use log::{debug, info, warn}; use pnet::datalink::{DataLinkReceiver, DataLinkSender, NetworkInterface}; use pnet::ipnetwork::Ipv4Network; use pnet::packet::arp::{ArpHardwareTypes, ArpOperations, ArpPacket, MutableArpPacket}; @@ -12,11 +13,12 @@ pub async fn send_packet( sender_ip: Ipv4Network, sender_macaddr: MacAddr, ) { + info!("Starting ARP sender loop"); for target_ip in sender_ip.iter() { if target_ip == sender_ip.ip() { continue; } - println!("Sending to {}", target_ip); + debug!("Sending ARP packet to {}", target_ip); for _ in 0..1 { //arp packet let mut arp_buf = [0u8; 28]; @@ -46,11 +48,13 @@ pub async fn send_packet( Some(interface.clone()), ); } - // sleep(Duration::from_millis(1)).await; + // Sleep 1 millisecond between IPs + sleep(Duration::from_millis(1)).await; } } pub async fn listen_for_packets(mut rx: Box, ipv4_net: Ipv4Network) { + info!("Starting ARP receiver loop"); loop { let arp_buffer = match rx.next() { Ok(buffer) => buffer, @@ -59,19 +63,20 @@ pub async fn listen_for_packets(mut rx: Box, ipv4_net: Ipv let ethernet_packet = EthernetPacket::new(arp_buffer).unwrap(); if ethernet_packet.get_ethertype() == EtherTypes::Arp { + debug!("ARP packet received"); let arp_packet = ArpPacket::new(ethernet_packet.payload()).unwrap(); if arp_packet.get_operation() == ArpOperations::Reply { + debug!("It is an ARP reply packet"); if arp_packet.get_target_proto_addr() == ipv4_net.ip() { - print!("."); - println!("Status ONLINE"); - println!( - "IP addr:\t{} \nMac addr:\t{}", + info!( + "Found online device - IP addr={} - MAC addr={}", arp_packet.get_sender_proto_addr(), arp_packet.get_sender_hw_addr() ); - println!("----------------------"); } } } + // Sleep 1 millisecond between IPs + sleep(Duration::from_millis(1)).await; } } diff --git a/src/main.rs b/src/main.rs index 0c50f2c..8d691db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,17 @@ +use log::{debug, info}; +mod config; mod device_finders; #[tokio::main] async fn main() { - let devices = device_finders::arp::find("wlp1s0").await; + env_logger::init(); + info!("Starting up oott"); + let devices = device_finders::arp::find("eno1").await; + + info!("Done with ARP probes"); for device in devices.iter() { - println!("{}", device); + debug!("Device found {}", device); } + info!("Exiting"); }