use pnet::packet::ethernet::{EtherTypes, EthernetPacket};
use pnet::packet::ipv4::Ipv4Packet;
use pnet::packet::ipv6::Ipv6Packet;
use tracing::debug;
#[derive(Debug)]
pub enum IpPacket<'a> {
Ipv4(&'a [u8]),
Ipv6(&'a [u8]),
None,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DatalinkFormat {
Ethernet,
RawIp,
Null,
}
pub fn parse_packet(packet: &[u8]) -> IpPacket<'_> {
try_ethernet_format(packet)
.or_else(|| try_raw_ip_format(packet))
.or_else(|| try_null_datalink_format(packet))
.unwrap_or(IpPacket::None)
}
fn try_ethernet_format(packet: &[u8]) -> Option<IpPacket<'_>> {
if packet.len() < 14 {
return None;
}
let ethernet = EthernetPacket::new(packet)?;
let ip_data = &packet[14..];
match ethernet.get_ethertype() {
EtherTypes::Ipv4 if Ipv4Packet::new(ip_data).is_some() => {
debug!("Parsed Ethernet IPv4 packet");
return Some(IpPacket::Ipv4(ip_data));
}
EtherTypes::Ipv6 if Ipv6Packet::new(ip_data).is_some() => {
debug!("Parsed Ethernet IPv6 packet");
return Some(IpPacket::Ipv6(ip_data));
}
_ => {}
}
None
}
fn try_raw_ip_format(packet: &[u8]) -> Option<IpPacket<'_>> {
if packet.len() < 20 {
return None;
}
let version = (packet[0] & 0xF0) >> 4;
match version {
4 if Ipv4Packet::new(packet).is_some() => {
debug!("Parsed Raw IPv4 packet");
return Some(IpPacket::Ipv4(packet));
}
6 if Ipv6Packet::new(packet).is_some() => {
debug!("Parsed Raw IPv6 packet");
return Some(IpPacket::Ipv6(packet));
}
_ => {}
}
None
}
fn try_null_datalink_format(packet: &[u8]) -> Option<IpPacket<'_>> {
if packet.len() < 24 || packet[0] != 0x1e || packet[1] != 0x00 {
return None;
}
let ip_data = &packet[4..]; let version = (ip_data[0] & 0xF0) >> 4;
match version {
4 if Ipv4Packet::new(ip_data).is_some() => {
debug!("Parsed NULL datalink IPv4 packet");
return Some(IpPacket::Ipv4(ip_data));
}
6 if Ipv6Packet::new(ip_data).is_some() => {
debug!("Parsed NULL datalink IPv6 packet");
return Some(IpPacket::Ipv6(ip_data));
}
_ => {}
}
None
}
pub fn detect_datalink_format(packet: &[u8]) -> Option<DatalinkFormat> {
if packet.len() >= 24 && packet[0] == 0x1e && packet[1] == 0x00 {
let ip_data = &packet[4..];
let version = (ip_data[0] & 0xF0) >> 4;
if version == 4 || version == 6 {
return Some(DatalinkFormat::Null);
}
}
if packet.len() >= 20 {
let version = (packet[0] & 0xF0) >> 4;
if version == 4 || version == 6 {
if version == 4 {
let ihl = (packet[0] & 0x0F).saturating_mul(4);
if ihl >= 20 && packet.len() >= usize::from(ihl) {
return Some(DatalinkFormat::RawIp);
}
}
else if version == 6 && packet.len() >= 40 {
return Some(DatalinkFormat::RawIp);
}
}
}
if packet.len() >= 14 {
if let Some(ethernet) = EthernetPacket::new(packet) {
let ethertype = ethernet.get_ethertype();
if ethertype == EtherTypes::Ipv4 || ethertype == EtherTypes::Ipv6 {
let ip_data = &packet[14..];
if !ip_data.is_empty() {
let version = (ip_data[0] & 0xF0) >> 4;
if (ethertype == EtherTypes::Ipv4 && version == 4)
|| (ethertype == EtherTypes::Ipv6 && version == 6)
{
return Some(DatalinkFormat::Ethernet);
}
}
}
}
}
None
}