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(Ipv4Packet<'a>),
Ipv6(Ipv6Packet<'a>),
None,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DatalinkFormat {
Ethernet,
RawIp,
Null,
}
#[inline]
pub fn parse_packet(packet: &[u8]) -> IpPacket<'_> {
if let Some(parsed) = try_ethernet_format(packet) {
return parsed;
}
if let Some(parsed) = try_raw_ip_format(packet) {
return parsed;
}
if let Some(parsed) = try_null_datalink_format(packet) {
return parsed;
}
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 let Some(ipv4) = Ipv4Packet::new(ip_data) {
debug!("Parsed Ethernet IPv4 packet");
return Some(IpPacket::Ipv4(ipv4));
}
}
EtherTypes::Ipv6 => {
if let Some(ipv6) = Ipv6Packet::new(ip_data) {
debug!("Parsed Ethernet IPv6 packet");
return Some(IpPacket::Ipv6(ipv6));
}
}
_ => {}
}
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 let Some(ipv4) = Ipv4Packet::new(packet) {
debug!("Parsed Raw IPv4 packet");
return Some(IpPacket::Ipv4(ipv4));
}
}
6 => {
if let Some(ipv6) = Ipv6Packet::new(packet) {
debug!("Parsed Raw IPv6 packet");
return Some(IpPacket::Ipv6(ipv6));
}
}
_ => {}
}
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 let Some(ipv4) = Ipv4Packet::new(ip_data) {
debug!("Parsed NULL datalink IPv4 packet");
return Some(IpPacket::Ipv4(ipv4));
}
}
6 => {
if let Some(ipv6) = Ipv6Packet::new(ip_data) {
debug!("Parsed NULL datalink IPv6 packet");
return Some(IpPacket::Ipv6(ipv6));
}
}
_ => {}
}
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
}