diff options
author | gennyble <gen@nyble.dev> | 2025-05-17 09:53:23 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2025-05-17 09:53:23 -0500 |
commit | 2bd66b9a04be8a63d7ee0be2d633ead183fa7e76 (patch) | |
tree | 8962e5bc5b0e8ac1af7f01f0db875bc60d8d44c3 /src/main.rs | |
download | skim-2bd66b9a04be8a63d7ee0be2d633ead183fa7e76.tar.gz skim-2bd66b9a04be8a63d7ee0be2d633ead183fa7e76.zip |
initial commit; meow
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..defd70b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,186 @@ +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<dyn Error>> { + 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<Meow>, +} + +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<Meow>) { + 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; + } + } + _ => (), + } + } +} |