use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::data::flow::{Direction, FlowKey, Protocol};
#[derive(Debug)]
pub struct ParsedPacket {
pub key: FlowKey,
pub direction: Direction,
pub len: u64,
}
pub fn parse_ethernet(data: &[u8], local_net: Option<(IpAddr, u8)>) -> Option<ParsedPacket> {
if data.len() < 14 {
return None;
}
let ethertype = u16::from_be_bytes([data[12], data[13]]);
let payload = &data[14..];
match ethertype {
0x0800 => parse_ipv4(payload, local_net),
0x86DD => parse_ipv6(payload, local_net),
0x8100 => {
if data.len() < 18 {
return None;
}
let inner_type = u16::from_be_bytes([data[16], data[17]]);
let vlan_payload = &data[18..];
match inner_type {
0x0800 => parse_ipv4(vlan_payload, local_net),
0x86DD => parse_ipv6(vlan_payload, local_net),
_ => None,
}
}
_ => None,
}
}
pub fn parse_loopback(data: &[u8], local_net: Option<(IpAddr, u8)>) -> Option<ParsedPacket> {
if data.len() < 4 {
return None;
}
let af = u32::from_ne_bytes([data[0], data[1], data[2], data[3]]);
let payload = &data[4..];
match af {
2 => parse_ipv4(payload, local_net), 30 => parse_ipv6(payload, local_net), 10 => parse_ipv6(payload, local_net), _ => None,
}
}
pub fn parse_sll(data: &[u8], local_net: Option<(IpAddr, u8)>) -> Option<ParsedPacket> {
if data.len() < 16 {
return None;
}
let protocol = u16::from_be_bytes([data[14], data[15]]);
let payload = &data[16..];
match protocol {
0x0800 => parse_ipv4(payload, local_net),
0x86DD => parse_ipv6(payload, local_net),
_ => None,
}
}
pub fn parse_raw(data: &[u8], local_net: Option<(IpAddr, u8)>) -> Option<ParsedPacket> {
if data.is_empty() {
return None;
}
let version = data[0] >> 4;
match version {
4 => parse_ipv4(data, local_net),
6 => parse_ipv6(data, local_net),
_ => None,
}
}
fn parse_ipv4(data: &[u8], local_net: Option<(IpAddr, u8)>) -> Option<ParsedPacket> {
if data.len() < 20 {
return None;
}
let ihl = ((data[0] & 0x0F) as usize) * 4;
let total_len = u16::from_be_bytes([data[2], data[3]]) as u64;
let protocol_num = data[9];
let src_ip = Ipv4Addr::new(data[12], data[13], data[14], data[15]);
let dst_ip = Ipv4Addr::new(data[16], data[17], data[18], data[19]);
let protocol = Protocol::from_ip_next_header(protocol_num);
let (src_port, dst_port) = parse_ports(protocol_num, data, ihl);
let src = IpAddr::V4(src_ip);
let dst = IpAddr::V4(dst_ip);
let direction = determine_direction(src, dst, local_net);
let (key_src, key_dst, key_src_port, key_dst_port) = match direction {
Direction::Sent => (src, dst, src_port, dst_port),
Direction::Received => (dst, src, dst_port, src_port),
};
Some(ParsedPacket {
key: FlowKey {
src: key_src,
dst: key_dst,
src_port: key_src_port,
dst_port: key_dst_port,
protocol,
},
direction,
len: total_len,
})
}
fn parse_ipv6(data: &[u8], local_net: Option<(IpAddr, u8)>) -> Option<ParsedPacket> {
if data.len() < 40 {
return None;
}
let payload_len = u16::from_be_bytes([data[4], data[5]]) as u64;
let total_len = payload_len + 40;
let next_header = data[6];
let src_ip = Ipv6Addr::from(<[u8; 16]>::try_from(&data[8..24]).ok()?);
let dst_ip = Ipv6Addr::from(<[u8; 16]>::try_from(&data[24..40]).ok()?);
let protocol = Protocol::from_ip_next_header(next_header);
let (src_port, dst_port) = parse_ports(next_header, data, 40);
let src = IpAddr::V6(src_ip);
let dst = IpAddr::V6(dst_ip);
let direction = determine_direction(src, dst, local_net);
let (key_src, key_dst, key_src_port, key_dst_port) = match direction {
Direction::Sent => (src, dst, src_port, dst_port),
Direction::Received => (dst, src, dst_port, src_port),
};
Some(ParsedPacket {
key: FlowKey {
src: key_src,
dst: key_dst,
src_port: key_src_port,
dst_port: key_dst_port,
protocol,
},
direction,
len: total_len,
})
}
fn parse_ports(protocol: u8, data: &[u8], header_len: usize) -> (u16, u16) {
match protocol {
6 | 17 => {
if data.len() >= header_len + 4 {
let src = u16::from_be_bytes([data[header_len], data[header_len + 1]]);
let dst = u16::from_be_bytes([data[header_len + 2], data[header_len + 3]]);
(src, dst)
} else {
(0, 0)
}
}
_ => (0, 0),
}
}
fn determine_direction(
src: IpAddr,
dst: IpAddr,
local_net: Option<(IpAddr, u8)>,
) -> Direction {
if let Some((net, prefix)) = local_net {
let src_local = ip_in_network(src, net, prefix);
let dst_local = ip_in_network(dst, net, prefix);
if src_local && !dst_local {
return Direction::Sent;
}
if !src_local && dst_local {
return Direction::Received;
}
}
Direction::Sent
}
pub(crate) fn ip_in_network(addr: IpAddr, network: IpAddr, prefix_len: u8) -> bool {
match (addr, network) {
(IpAddr::V4(a), IpAddr::V4(n)) => {
if prefix_len >= 32 {
return a == n;
}
let mask = !0u32 << (32 - prefix_len);
(u32::from(a) & mask) == (u32::from(n) & mask)
}
(IpAddr::V6(a), IpAddr::V6(n)) => {
if prefix_len >= 128 {
return a == n;
}
let mask = !0u128 << (128 - prefix_len);
(u128::from(a) & mask) == (u128::from(n) & mask)
}
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_ipv4_tcp_packet(src: [u8; 4], dst: [u8; 4], src_port: u16, dst_port: u16) -> Vec<u8> {
let mut pkt = vec![0u8; 44]; pkt[12] = 0x08; pkt[13] = 0x00; pkt[14] = 0x45; pkt[16] = 0x00; pkt[17] = 30; pkt[23] = 6; pkt[26..30].copy_from_slice(&src);
pkt[30..34].copy_from_slice(&dst);
pkt[34] = (src_port >> 8) as u8; pkt[35] = src_port as u8;
pkt[36] = (dst_port >> 8) as u8; pkt[37] = dst_port as u8;
pkt
}
#[test]
fn parse_ethernet_ipv4_tcp() {
let pkt = make_ipv4_tcp_packet([10, 0, 0, 1], [10, 0, 0, 2], 12345, 80);
let result = parse_ethernet(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Tcp);
assert_eq!(result.key.src_port, 12345);
assert_eq!(result.key.dst_port, 80);
assert_eq!(result.len, 30);
}
#[test]
fn parse_ethernet_too_short() {
assert!(parse_ethernet(&[0; 10], None).is_none());
}
#[test]
fn parse_ethernet_unknown_ethertype() {
let mut pkt = vec![0u8; 60];
pkt[12] = 0xFF; pkt[13] = 0xFF;
assert!(parse_ethernet(&pkt, None).is_none());
}
#[test]
fn parse_raw_ipv4() {
let mut raw = vec![0u8; 30];
raw[0] = 0x45; raw[2] = 0; raw[3] = 30;
raw[9] = 17; raw[12..16].copy_from_slice(&[192, 168, 1, 1]);
raw[16..20].copy_from_slice(&[8, 8, 8, 8]);
raw[20] = 0x1F; raw[21] = 0x90; raw[22] = 0x00; raw[23] = 0x35; let result = parse_raw(&raw, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Udp);
assert_eq!(result.key.src_port, 8080);
assert_eq!(result.key.dst_port, 53);
}
#[test]
fn parse_raw_empty() {
assert!(parse_raw(&[], None).is_none());
}
#[test]
fn parse_loopback_too_short() {
assert!(parse_loopback(&[0, 0], None).is_none());
}
#[test]
fn parse_sll_too_short() {
assert!(parse_sll(&[0; 10], None).is_none());
}
#[test]
fn ip_in_network_ipv4_match() {
let addr: IpAddr = "192.168.1.100".parse().unwrap();
let net: IpAddr = "192.168.1.0".parse().unwrap();
assert!(ip_in_network(addr, net, 24));
}
#[test]
fn ip_in_network_ipv4_no_match() {
let addr: IpAddr = "192.168.2.100".parse().unwrap();
let net: IpAddr = "192.168.1.0".parse().unwrap();
assert!(!ip_in_network(addr, net, 24));
}
#[test]
fn ip_in_network_ipv4_wide_mask() {
let addr: IpAddr = "10.255.255.255".parse().unwrap();
let net: IpAddr = "10.0.0.0".parse().unwrap();
assert!(ip_in_network(addr, net, 8));
}
#[test]
fn ip_in_network_ipv4_host_mask() {
let addr: IpAddr = "10.0.0.1".parse().unwrap();
let net: IpAddr = "10.0.0.1".parse().unwrap();
assert!(ip_in_network(addr, net, 32));
}
#[test]
fn ip_in_network_mixed_families() {
let v4: IpAddr = "10.0.0.1".parse().unwrap();
let v6: IpAddr = "::1".parse().unwrap();
assert!(!ip_in_network(v4, v6, 8));
}
#[test]
fn direction_with_local_net() {
let local: IpAddr = "192.168.1.0".parse().unwrap();
let pkt = make_ipv4_tcp_packet([192, 168, 1, 5], [8, 8, 8, 8], 5000, 443);
let result = parse_ethernet(&pkt, Some((local, 24))).unwrap();
assert_eq!(result.direction, Direction::Sent);
}
#[test]
fn direction_received() {
let local: IpAddr = "192.168.1.0".parse().unwrap();
let pkt = make_ipv4_tcp_packet([8, 8, 8, 8], [192, 168, 1, 5], 443, 5000);
let result = parse_ethernet(&pkt, Some((local, 24))).unwrap();
assert_eq!(result.direction, Direction::Received);
}
fn make_vlan_ipv4_tcp_packet(src: [u8; 4], dst: [u8; 4], src_port: u16, dst_port: u16) -> Vec<u8> {
let mut pkt = vec![0u8; 48]; pkt[12] = 0x81; pkt[13] = 0x00; pkt[14] = 0x00; pkt[15] = 0x64; pkt[16] = 0x08; pkt[17] = 0x00; pkt[18] = 0x45; pkt[20] = 0x00; pkt[21] = 30; pkt[27] = 6; pkt[30..34].copy_from_slice(&src);
pkt[34..38].copy_from_slice(&dst);
pkt[38] = (src_port >> 8) as u8; pkt[39] = src_port as u8;
pkt[40] = (dst_port >> 8) as u8; pkt[41] = dst_port as u8;
pkt
}
#[test]
fn parse_vlan_ipv4_tcp() {
let pkt = make_vlan_ipv4_tcp_packet([10, 0, 0, 1], [10, 0, 0, 2], 12345, 80);
let result = parse_ethernet(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Tcp);
assert_eq!(result.key.src_port, 12345);
assert_eq!(result.key.dst_port, 80);
}
#[test]
fn parse_vlan_too_short() {
let mut pkt = vec![0u8; 16]; pkt[12] = 0x81; pkt[13] = 0x00;
assert!(parse_ethernet(&pkt, None).is_none());
}
#[test]
fn parse_vlan_unknown_inner_ethertype() {
let mut pkt = vec![0u8; 60];
pkt[12] = 0x81; pkt[13] = 0x00; pkt[16] = 0xFF; pkt[17] = 0xFF; assert!(parse_ethernet(&pkt, None).is_none());
}
fn make_ipv6_tcp_packet() -> Vec<u8> {
let mut pkt = vec![0u8; 14 + 40 + 4]; pkt[12] = 0x86; pkt[13] = 0xDD; pkt[14] = 0x60; pkt[18] = 0x00; pkt[19] = 4; pkt[20] = 6; pkt[21] = 64; pkt[37] = 1;
pkt[53] = 2;
pkt[54] = 0x1F; pkt[55] = 0x90; pkt[56] = 0x00; pkt[57] = 0x50; pkt
}
#[test]
fn parse_ethernet_ipv6_tcp() {
let pkt = make_ipv6_tcp_packet();
let result = parse_ethernet(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Tcp);
assert_eq!(result.key.src_port, 8080);
assert_eq!(result.key.dst_port, 80);
assert_eq!(result.len, 44); }
#[test]
fn parse_ethernet_ipv6_too_short() {
let mut pkt = vec![0u8; 30]; pkt[12] = 0x86; pkt[13] = 0xDD;
assert!(parse_ethernet(&pkt, None).is_none());
}
#[test]
fn parse_raw_ipv6() {
let mut raw = vec![0u8; 44]; raw[0] = 0x60; raw[4] = 0x00; raw[5] = 4; raw[6] = 17; raw[7] = 64;
raw[23] = 1; raw[39] = 2; raw[40] = 0x00; raw[41] = 0x35; raw[42] = 0x1F; raw[43] = 0x90; let result = parse_raw(&raw, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Udp);
assert_eq!(result.key.src_port, 53);
assert_eq!(result.key.dst_port, 8080);
}
#[test]
fn parse_raw_unknown_version() {
let raw = vec![0x30; 20]; assert!(parse_raw(&raw, None).is_none());
}
#[test]
fn parse_loopback_af_inet() {
let mut pkt = vec![0u8; 24 + 4]; let af_bytes = 2u32.to_ne_bytes();
pkt[0..4].copy_from_slice(&af_bytes);
pkt[4] = 0x45;
pkt[6] = 0; pkt[7] = 24; pkt[13] = 6; pkt[16..20].copy_from_slice(&[127, 0, 0, 1]);
pkt[20..24].copy_from_slice(&[127, 0, 0, 1]);
pkt[24] = 0x1F; pkt[25] = 0x90; pkt[26] = 0x00; pkt[27] = 0x50; let result = parse_loopback(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Tcp);
assert_eq!(result.key.src_port, 8080);
assert_eq!(result.key.dst_port, 80);
}
#[test]
fn parse_loopback_unknown_af() {
let mut pkt = vec![0u8; 60];
let af_bytes = 99u32.to_ne_bytes();
pkt[0..4].copy_from_slice(&af_bytes);
assert!(parse_loopback(&pkt, None).is_none());
}
#[test]
fn parse_sll_ipv4() {
let mut pkt = vec![0u8; 16 + 24]; pkt[14] = 0x08; pkt[15] = 0x00; pkt[16] = 0x45;
pkt[18] = 0; pkt[19] = 24;
pkt[25] = 17; pkt[28..32].copy_from_slice(&[10, 0, 0, 1]);
pkt[32..36].copy_from_slice(&[10, 0, 0, 2]);
pkt[36] = 0x00; pkt[37] = 0x35; pkt[38] = 0x1F; pkt[39] = 0x90; let result = parse_sll(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Udp);
assert_eq!(result.key.src_port, 53);
}
#[test]
fn parse_sll_unknown_protocol() {
let mut pkt = vec![0u8; 60];
pkt[14] = 0xFF; pkt[15] = 0xFF;
assert!(parse_sll(&pkt, None).is_none());
}
#[test]
fn direction_both_local_defaults_sent() {
let local: IpAddr = "10.0.0.0".parse().unwrap();
let pkt = make_ipv4_tcp_packet([10, 0, 0, 1], [10, 0, 0, 2], 5000, 80);
let result = parse_ethernet(&pkt, Some((local, 8))).unwrap();
assert_eq!(result.direction, Direction::Sent);
}
#[test]
fn direction_both_remote_defaults_sent() {
let local: IpAddr = "192.168.1.0".parse().unwrap();
let pkt = make_ipv4_tcp_packet([8, 8, 8, 8], [1, 1, 1, 1], 443, 80);
let result = parse_ethernet(&pkt, Some((local, 24))).unwrap();
assert_eq!(result.direction, Direction::Sent);
}
#[test]
fn direction_no_local_net_defaults_sent() {
let pkt = make_ipv4_tcp_packet([1, 2, 3, 4], [5, 6, 7, 8], 80, 443);
let result = parse_ethernet(&pkt, None).unwrap();
assert_eq!(result.direction, Direction::Sent);
}
#[test]
fn ip_in_network_ipv6_match() {
let addr: IpAddr = "2001:db8::1".parse().unwrap();
let net: IpAddr = "2001:db8::".parse().unwrap();
assert!(ip_in_network(addr, net, 32));
}
#[test]
fn ip_in_network_ipv6_no_match() {
let addr: IpAddr = "2001:db9::1".parse().unwrap();
let net: IpAddr = "2001:db8::".parse().unwrap();
assert!(!ip_in_network(addr, net, 32));
}
#[test]
fn ip_in_network_ipv6_host_mask() {
let addr: IpAddr = "::1".parse().unwrap();
let net: IpAddr = "::1".parse().unwrap();
assert!(ip_in_network(addr, net, 128));
}
#[test]
fn ip_in_network_ipv6_slash64() {
let addr: IpAddr = "fe80::abcd:1234".parse().unwrap();
let net: IpAddr = "fe80::".parse().unwrap();
assert!(ip_in_network(addr, net, 64));
}
#[test]
fn ip_in_network_ipv6_slash64_no_match() {
let addr: IpAddr = "fe81::1".parse().unwrap();
let net: IpAddr = "fe80::".parse().unwrap();
assert!(!ip_in_network(addr, net, 64));
}
#[test]
fn parse_icmp_has_no_ports() {
let mut raw = vec![0u8; 28]; raw[0] = 0x45;
raw[2] = 0; raw[3] = 28;
raw[9] = 1; raw[12..16].copy_from_slice(&[10, 0, 0, 1]);
raw[16..20].copy_from_slice(&[10, 0, 0, 2]);
let result = parse_raw(&raw, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Icmp);
assert_eq!(result.key.src_port, 0);
assert_eq!(result.key.dst_port, 0);
}
#[test]
fn parse_truncated_tcp_ports() {
let mut raw = vec![0u8; 20];
raw[0] = 0x45;
raw[2] = 0; raw[3] = 20;
raw[9] = 6; raw[12..16].copy_from_slice(&[10, 0, 0, 1]);
raw[16..20].copy_from_slice(&[10, 0, 0, 2]);
let result = parse_raw(&raw, None).unwrap();
assert_eq!(result.key.src_port, 0);
assert_eq!(result.key.dst_port, 0);
}
#[test]
fn parse_ipv4_udp_packet() {
let mut pkt = vec![0u8; 44];
pkt[12] = 0x08; pkt[13] = 0x00; pkt[14] = 0x45;
pkt[16] = 0x00; pkt[17] = 28;
pkt[23] = 17; pkt[26..30].copy_from_slice(&[10, 0, 0, 1]);
pkt[30..34].copy_from_slice(&[8, 8, 8, 8]);
pkt[34] = 0xC0; pkt[35] = 0x00; pkt[36] = 0x00; pkt[37] = 0x35; let result = parse_ethernet(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Udp);
assert_eq!(result.key.src_port, 49152);
assert_eq!(result.key.dst_port, 53);
}
#[test]
fn received_direction_reverses_key() {
let local: IpAddr = "192.168.1.0".parse().unwrap();
let pkt = make_ipv4_tcp_packet([8, 8, 8, 8], [192, 168, 1, 5], 443, 5000);
let result = parse_ethernet(&pkt, Some((local, 24))).unwrap();
assert_eq!(result.key.src, "192.168.1.5".parse::<IpAddr>().unwrap());
assert_eq!(result.key.dst, "8.8.8.8".parse::<IpAddr>().unwrap());
assert_eq!(result.key.src_port, 5000);
assert_eq!(result.key.dst_port, 443);
}
#[test]
fn parse_vlan_ipv6() {
let mut pkt = vec![0u8; 18 + 40 + 4]; pkt[12] = 0x81; pkt[13] = 0x00; pkt[16] = 0x86; pkt[17] = 0xDD; pkt[18] = 0x60;
pkt[22] = 0; pkt[23] = 4; pkt[24] = 17; pkt[41] = 1; pkt[57] = 2; pkt[58] = 0x00; pkt[59] = 0x35; pkt[60] = 0x1F; pkt[61] = 0x90; let result = parse_ethernet(&pkt, None).unwrap();
assert_eq!(result.key.protocol, Protocol::Udp);
}
}