use clap::Parser;
use crossterm::{
cursor::MoveTo,
execute,
terminal::{Clear, ClearType},
};
use ndisapi::{AsyncNdisapiAdapter, DirectionFlags, FilterFlags, IntermediateBuffer, Ndisapi};
use prettytable::{row, Table};
use smoltcp::wire::{
ArpPacket, EthernetFrame, EthernetProtocol, Icmpv4Message, Icmpv4Packet, Icmpv6Message,
Icmpv6Packet, IpAddress, IpProtocol, Ipv4Address, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket,
};
use std::{collections::HashMap, fmt, sync::Arc};
use tokio::{
sync::{
mpsc::{self, Receiver},
oneshot, Mutex,
},
time::{self, Duration},
};
use windows::core::Result;
const PACKET_NUMBER: usize = 510;
#[derive(Hash, Eq, PartialEq, Debug)]
struct PacketInfo {
ethertype: EthernetProtocol,
src_addr: Option<IpAddress>,
dst_addr: Option<IpAddress>,
protocol: Option<IpProtocol>,
src_port: Option<u16>,
dst_port: Option<u16>,
icmp_v4_type: Option<Icmpv4Message>,
icmp_v6_type: Option<Icmpv6Message>,
icmp_code: Option<u8>,
}
impl Default for PacketInfo {
fn default() -> Self {
PacketInfo {
ethertype: EthernetProtocol::Unknown(0), src_addr: None,
dst_addr: None,
protocol: None,
src_port: None,
dst_port: None,
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
}
}
}
impl PacketInfo {
fn handle_ipv4_packet(eth_hdr: &EthernetFrame<&[u8]>) -> PacketInfo {
let ipv4_packet = Ipv4Packet::new_unchecked(eth_hdr.payload());
match ipv4_packet.next_header() {
IpProtocol::Icmp => {
let icmp_packet = Icmpv4Packet::new_unchecked(ipv4_packet.payload());
PacketInfo {
ethertype: EthernetProtocol::Ipv4,
src_addr: Some(IpAddress::Ipv4(ipv4_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv4(ipv4_packet.dst_addr())),
protocol: Some(IpProtocol::Icmp),
src_port: None,
dst_port: None,
icmp_v4_type: Some(icmp_packet.msg_type()),
icmp_v6_type: None,
icmp_code: Some(icmp_packet.msg_code()),
}
}
IpProtocol::Tcp => {
let tcp_packet = TcpPacket::new_unchecked(ipv4_packet.payload());
PacketInfo {
ethertype: EthernetProtocol::Ipv4,
src_addr: Some(IpAddress::Ipv4(ipv4_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv4(ipv4_packet.dst_addr())),
protocol: Some(IpProtocol::Tcp),
src_port: Some(tcp_packet.src_port()),
dst_port: Some(tcp_packet.dst_port()),
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
}
}
IpProtocol::Udp => {
let udp_packet = UdpPacket::new_unchecked(ipv4_packet.payload());
PacketInfo {
ethertype: EthernetProtocol::Ipv4,
src_addr: Some(IpAddress::Ipv4(ipv4_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv4(ipv4_packet.dst_addr())),
protocol: Some(IpProtocol::Udp),
src_port: Some(udp_packet.src_port()),
dst_port: Some(udp_packet.dst_port()),
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
}
}
_ => PacketInfo {
ethertype: EthernetProtocol::Ipv4,
src_addr: Some(IpAddress::Ipv4(ipv4_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv4(ipv4_packet.dst_addr())),
protocol: Some(ipv4_packet.next_header()),
src_port: None,
dst_port: None,
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
},
}
}
fn handle_ipv6_packet(eth_hdr: &EthernetFrame<&[u8]>) -> PacketInfo {
let ipv6_packet = Ipv6Packet::new_unchecked(eth_hdr.payload());
match ipv6_packet.next_header() {
IpProtocol::Icmpv6 => {
let icmp_packet = Icmpv6Packet::new_unchecked(ipv6_packet.payload());
PacketInfo {
ethertype: EthernetProtocol::Ipv6,
src_addr: Some(IpAddress::Ipv6(ipv6_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv6(ipv6_packet.dst_addr())),
protocol: Some(IpProtocol::Icmpv6),
src_port: None,
dst_port: None,
icmp_v4_type: None,
icmp_v6_type: Some(icmp_packet.msg_type()),
icmp_code: Some(icmp_packet.msg_code()),
}
}
IpProtocol::Tcp => {
let tcp_packet = TcpPacket::new_unchecked(ipv6_packet.payload());
PacketInfo {
ethertype: EthernetProtocol::Ipv6,
src_addr: Some(IpAddress::Ipv6(ipv6_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv6(ipv6_packet.dst_addr())),
protocol: Some(IpProtocol::Tcp),
src_port: Some(tcp_packet.src_port()),
dst_port: Some(tcp_packet.dst_port()),
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
}
}
IpProtocol::Udp => {
let udp_packet = UdpPacket::new_unchecked(ipv6_packet.payload());
PacketInfo {
ethertype: EthernetProtocol::Ipv6,
src_addr: Some(IpAddress::Ipv6(ipv6_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv6(ipv6_packet.dst_addr())),
protocol: Some(IpProtocol::Udp),
src_port: Some(udp_packet.src_port()),
dst_port: Some(udp_packet.dst_port()),
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
}
}
_ => PacketInfo {
ethertype: EthernetProtocol::Ipv6,
src_addr: Some(IpAddress::Ipv6(ipv6_packet.src_addr())),
dst_addr: Some(IpAddress::Ipv6(ipv6_packet.dst_addr())),
protocol: Some(ipv6_packet.next_header()),
src_port: None,
dst_port: None,
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
},
}
}
fn handle_arp_packet(eth_hdr: &EthernetFrame<&[u8]>) -> PacketInfo {
let arp_packet = ArpPacket::new_unchecked(eth_hdr.payload());
let src_bytes: [u8; 4] = arp_packet
.source_protocol_addr()
.try_into()
.unwrap_or([0u8; 4]);
let dst_bytes: [u8; 4] = arp_packet
.target_protocol_addr()
.try_into()
.unwrap_or([0u8; 4]);
PacketInfo {
ethertype: EthernetProtocol::Arp,
src_addr: Some(IpAddress::Ipv4(Ipv4Address::from_octets(src_bytes))),
dst_addr: Some(IpAddress::Ipv4(Ipv4Address::from_octets(dst_bytes))),
protocol: None,
src_port: None,
dst_port: None,
icmp_v4_type: None,
icmp_v6_type: None,
icmp_code: None,
}
}
pub fn new(packet: &IntermediateBuffer) -> Self {
let eth_hdr = EthernetFrame::new_unchecked(packet.get_data());
match eth_hdr.ethertype() {
EthernetProtocol::Ipv4 => Self::handle_ipv4_packet(ð_hdr),
EthernetProtocol::Ipv6 => Self::handle_ipv6_packet(ð_hdr),
EthernetProtocol::Arp => Self::handle_arp_packet(ð_hdr),
_ => {
PacketInfo {
ethertype: eth_hdr.ethertype(),
..PacketInfo::default()
}
}
}
}
}
impl fmt::Display for PacketInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Ethertype: {:?}\nSource Address: {:?}\nDestination Address: {:?}\nProtocol: {:?}\n",
self.ethertype, self.src_addr, self.dst_addr, self.protocol
)?;
if let Some(src_port) = self.src_port {
writeln!(f, "Source Port: {}", src_port)?;
}
if let Some(dst_port) = self.dst_port {
writeln!(f, "Destination Port: {}", dst_port)?;
}
if let Some(icmp_v4_type) = self.icmp_v4_type {
writeln!(f, "ICMPv4 Type: {:?}", icmp_v4_type)?;
}
if let Some(icmp_v6_type) = self.icmp_v6_type {
writeln!(f, "ICMPv6 Type: {:?}", icmp_v6_type)?;
}
if let Some(icmp_code) = self.icmp_code {
writeln!(f, "ICMP Code: {:?}", icmp_code)?;
}
Ok(())
}
}
async fn async_loop(adapter: &mut AsyncNdisapiAdapter, tx: mpsc::Sender<PacketInfo>) -> Result<()> {
adapter.set_adapter_mode(FilterFlags::MSTCP_FLAG_SENT_RECEIVE_TUNNEL)?;
let mut packets: Vec<IntermediateBuffer> = vec![Default::default(); PACKET_NUMBER];
loop {
let packets_read = match adapter.read_packets::<PACKET_NUMBER>(&mut packets).await {
Ok(packets_read) => {
if packets_read == 0 {
println!("No packets read. Continue reading.");
continue;
} else {
packets_read
}
}
Err(_) => {
continue;
}
};
for packet in packets[0..packets_read].iter() {
let packet_info = PacketInfo::new(packet);
tx.send(packet_info).await.unwrap();
}
let (send_packets, receive_packets): (Vec<_>, Vec<_>) = packets[0..packets_read]
.iter()
.partition(|ib| ib.get_device_flags() == DirectionFlags::PACKET_FLAG_ON_SEND);
match adapter.send_packets_to_adapter::<PACKET_NUMBER>(send_packets) {
Ok(_) => {}
Err(err) => println!("Error sending packet to adapter. Error code = {err}"),
}
match adapter.send_packets_to_mstcp::<PACKET_NUMBER>(receive_packets) {
Ok(_) => {}
Err(err) => println!("Error sending packet to adapter. Error code = {err}"),
}
}
}
async fn update_display(shared_table: Arc<Mutex<HashMap<PacketInfo, u32>>>) {
let mut interval = time::interval(Duration::from_secs(1));
loop {
interval.tick().await;
let packet_info_table = shared_table.lock().await;
execute!(std::io::stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap();
let mut table = Table::new();
table.add_row(row!["Protocol", "Source", "Destination", "Count"]);
let mut counts: Vec<_> = packet_info_table
.iter()
.map(|(pi, &count)| (pi, count))
.collect();
counts.sort_by(|a, b| b.1.cmp(&a.1));
let top_entries = &counts[..std::cmp::min(10, counts.len())];
for (packet_info, count) in top_entries {
let src = format!(
"{}:{}",
packet_info
.src_addr
.unwrap_or(IpAddress::Ipv4(Ipv4Address::new(0, 0, 0, 0))),
packet_info.src_port.unwrap_or_default()
);
let dst = format!(
"{}:{}",
packet_info
.dst_addr
.unwrap_or(IpAddress::Ipv4(Ipv4Address::new(0, 0, 0, 0))),
packet_info.dst_port.unwrap_or_default()
);
table.add_row(row![
packet_info.protocol.unwrap_or(IpProtocol::Unknown(0)),
src,
dst,
count.to_string()
]);
}
table.printstd();
}
}
async fn process_packet_logs(mut log_rx: Receiver<PacketInfo>) {
let packet_info_table = Arc::new(Mutex::new(HashMap::new()));
tokio::spawn(update_display(packet_info_table.clone()));
loop {
if let Some(log_message) = log_rx.recv().await {
let mut table = packet_info_table.lock().await;
*table.entry(log_message).or_insert(0) += 1;
}
}
}
async fn main_async(adapter: &mut AsyncNdisapiAdapter) {
println!("Press ENTER to exit");
let (tx, rx) = oneshot::channel::<()>();
let (log_tx, log_rx) = mpsc::channel::<PacketInfo>(PACKET_NUMBER * 100);
tokio::spawn(async move {
let mut line = String::new();
std::io::stdin().read_line(&mut line).unwrap();
let _ = tx.send(());
});
let result = tokio::select! {
result = async_loop(adapter, log_tx) => result,
_ = process_packet_logs(log_rx) => Ok(()),
_ = rx => {
println!("Shutting down...");
Ok(()) }
};
if let Err(e) = result {
eprintln!("Server error: {}", e);
}
}
#[derive(Parser)]
struct Cli {
#[clap(short, long)]
interface_index: usize,
}
#[tokio::main]
async fn main() -> Result<()> {
let Cli {
mut interface_index,
} = Cli::parse();
interface_index -= 1;
let driver = Arc::new(
Ndisapi::new("NDISRD").expect("WinpkFilter driver is not installed or failed to load!"),
);
println!(
"Detected Windows Packet Filter version {}",
driver.get_version()?
);
let adapters = driver.get_tcpip_bound_adapters_info()?;
if interface_index + 1 > adapters.len() {
panic!("Interface index is beyond the number of available interfaces");
}
println!("Using interface {}", adapters[interface_index].get_name(),);
let mut adapter =
AsyncNdisapiAdapter::new(Arc::clone(&driver), adapters[interface_index].get_handle())
.unwrap();
main_async(&mut adapter).await;
Ok(())
}