use std::{ error::Error, net::{Ipv4Addr, Shutdown}, sync::mpsc::{self, Receiver, Sender, channel}, thread::JoinHandle, time::{Duration, Instant}, }; use ethertype::EtherType; use ippacket::{IpNextHeader, Ipv4Packet}; use layer3::{Tcp, Udp}; use pnet_datalink::{Channel, Config}; use scurvy::{Argument, Scurvy}; mod ethertype; mod ippacket; mod layer3; fn main() -> Result<(), Box> { let args = vec![ Argument::new(&["interface", "iface"]).arg("dev"), Argument::new("ip").arg("addr"), ]; let scurvy = Scurvy::make(args); let interface_name = scurvy.get("interface").unwrap(); let interface = match pnet_datalink::interfaces().iter().find(|i| i.name == interface_name) { None => { eprintln!("No interface found named '{interface_name}'"); return Ok(()); } Some(i) => i.clone(), }; let ip_want: Ipv4Addr = scurvy.get("ip").unwrap().parse()?; let mut channel = match pnet_datalink::channel(&interface, Config::default())? { Channel::Ethernet(_tx, rx) => rx, _ => unimplemented!(), }; let stat = stat_thread(); let tx_clone = stat.tx.clone(); std::thread::spawn(move || { std::thread::sleep(Duration::from_secs(30)); tx_clone.send(Meow::Show).unwrap(); }); loop { let pkt = channel.next()?; let ethertype = EtherType::new(u16::from_be_bytes([pkt[12], pkt[13]])); let eth_payload = &pkt[14..]; match ethertype { EtherType::IPv4 => { let ip = Ipv4Packet::new(eth_payload); let ip_payload = ip.get_payload(); // 6 byte per MAC (x2), 2 byte ethertype, 2 byte crc let total_l2_len = ip.get_packet_len() + 18; match ip.get_next_header() { IpNextHeader::Tcp => { let tcp = Tcp::new(ip_payload); let tcp_tx = if ip.src == ip_want { true } else if ip.dst == ip_want { false } else { continue; }; stat.tx.send(Meow::Tcp { tx: tcp_tx, src: tcp.source_port(), dst: tcp.destination_port(), len: total_l2_len, })?; } IpNextHeader::Udp => { let udp = Udp::new(ip_payload); let udp_tx = if ip.src == ip_want { true } else if ip.dst == ip_want { false } else { continue; }; stat.tx.send(Meow::Udp { tx: udp_tx, src: udp.source_port(), dst: udp.destination_port(), len: total_l2_len, })?; } _ => (), } } _ => (), } } } struct StatHandle { hwnd: JoinHandle<()>, tx: Sender, } enum Meow { Tcp { tx: bool, src: u16, dst: u16, len: usize, }, Udp { tx: bool, src: u16, dst: u16, len: usize, }, Show, Shutdown, } fn stat_thread() -> StatHandle { let (tx, rx) = channel(); let hwnd = std::thread::spawn(|| stat(rx)); StatHandle { hwnd, tx } } fn stat(rx: Receiver) { let mut tcp_tx = vec![0; u16::MAX as usize]; let mut tcp_rx = vec![0; u16::MAX as usize]; let mut last_print = Instant::now(); loop { if last_print.elapsed() > Duration::from_secs(10) { let http_s = tcp_rx[80] + tcp_rx[443]; let http_s_kb = http_s / 1000; let http_s_tx = tcp_tx[80] + tcp_tx[443]; let http_s_tx_kb = http_s_tx / 1000; println!("HTTP(S) rx {}kB // tx {}kB", http_s_kb, http_s_tx_kb); last_print = Instant::now(); } match rx.recv() { Err(_e) => { eprintln!("error receiving! breaking from loop"); break; } Ok(Meow::Show) => { if last_print.elapsed() > Duration::from_secs(30) { println!("Activated by Meow::Show"); } } Ok(Meow::Shutdown) => { eprintln!("got shutdown! breaking from loop"); break; } Ok(Meow::Tcp { tx: tcp_tx_flag, src, dst, len, }) => { if tcp_tx_flag { tcp_tx[src as usize] += len; } else { tcp_rx[dst as usize] += len; } } _ => (), } } }