use std::net::IpAddr;
use etherparse::{LaxNetSlice, LaxSlicedPacket, TransportSlice};
use log::debug;
use super::FlowKey;
use crate::capture::Link;
#[derive(Debug, Clone)]
pub struct PacketInfo {
pub flow_key: FlowKey,
pub packet_size: u64,
pub tcp_flags: u16,
}
pub fn parse(link: Link, data: &[u8]) -> Option<PacketInfo> {
let sliced = match link {
Link::Ethernet => LaxSlicedPacket::from_ethernet(data).map_err(|e| e.to_string()),
Link::Ip => LaxSlicedPacket::from_ip(data).map_err(|e| e.to_string()),
};
match sliced {
Ok(sliced) => packet_info(&sliced),
Err(e) => {
debug!("Failed to parse packet: {}", e);
None
}
}
}
fn packet_info(sliced: &LaxSlicedPacket) -> Option<PacketInfo> {
let (source_ip, dest_ip, protocol, total_length) = match &sliced.net {
Some(LaxNetSlice::Ipv4(ipv4)) => {
let header = ipv4.header();
(
IpAddr::V4(header.source_addr()),
IpAddr::V4(header.destination_addr()),
header.protocol().0,
header.total_len() as u64,
)
}
Some(LaxNetSlice::Ipv6(ipv6)) => {
let header = ipv6.header();
(
IpAddr::V6(header.source_addr()),
IpAddr::V6(header.destination_addr()),
ipv6.payload().ip_number.0,
u64::from(header.payload_length()) + 40,
)
}
Some(LaxNetSlice::Arp(_)) | None => {
debug!("No IP layer found");
return None;
}
};
let (source_port, dest_port, tcp_flags) = match &sliced.transport {
Some(TransportSlice::Tcp(tcp)) => {
let header = tcp.to_header();
let flags = (header.ns as u16) << 8
| (header.fin as u16)
| ((header.syn as u16) << 1)
| ((header.rst as u16) << 2)
| ((header.psh as u16) << 3)
| ((header.ack as u16) << 4)
| ((header.urg as u16) << 5)
| ((header.ece as u16) << 6)
| ((header.cwr as u16) << 7);
(header.source_port, header.destination_port, flags)
}
Some(TransportSlice::Udp(udp)) => {
let header = udp.to_header();
(header.source_port, header.destination_port, 0)
}
_ => (0, 0, 0),
};
Some(PacketInfo {
flow_key: FlowKey {
source_ip,
destination_ip: dest_ip,
protocol,
source_port,
destination_port: dest_port,
},
packet_size: total_length,
tcp_flags,
})
}