about summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs186
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;
+				}
+			}
+			_ => (),
+		}
+	}
+}