#![cfg(all(feature = "extractors", feature = "test-helpers"))]
use flowscope::{
extract::parse::test_frames::{ipv4_tcp, ipv4_udp},
layers::{Layer, LayerKind, Layers},
};
fn arp_request_frame() -> Vec<u8> {
let mut f = vec![0u8; 14 + 28];
f[0..6].copy_from_slice(&[0xff; 6]); f[6..12].copy_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
f[12..14].copy_from_slice(&0x0806u16.to_be_bytes()); f[14..16].copy_from_slice(&1u16.to_be_bytes()); f[16..18].copy_from_slice(&0x0800u16.to_be_bytes()); f[18] = 6; f[19] = 4; f[20..22].copy_from_slice(&1u16.to_be_bytes()); f[22..28].copy_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
f[28..32].copy_from_slice(&[192, 168, 1, 100]);
f[32..38].copy_from_slice(&[0; 6]);
f[38..42].copy_from_slice(&[192, 168, 1, 1]);
f
}
#[test]
fn arp_request_is_recognized() {
let f = arp_request_frame();
let layers = Layers::parse_ethernet(&f).unwrap();
let arp = layers.arp().expect("ARP slice");
assert_eq!(arp.htype(), 1);
assert_eq!(arp.ptype(), 0x0800);
assert_eq!(arp.oper(), 1);
assert_eq!(
arp.sender_pa().unwrap(),
std::net::Ipv4Addr::new(192, 168, 1, 100)
);
assert_eq!(
arp.target_pa().unwrap(),
std::net::Ipv4Addr::new(192, 168, 1, 1)
);
assert!(layers.ipv4().is_none());
assert!(layers.tcp().is_none());
}
fn vxlan_wrapped_frame() -> Vec<u8> {
let inner = ipv4_tcp(
[11; 6],
[12; 6],
[10, 0, 0, 1],
[10, 0, 0, 2],
12345,
80,
1000,
0,
0x02,
b"",
);
let mut vxlan = vec![0x08, 0, 0, 0, 0, 0, 0x2a, 0];
vxlan.extend_from_slice(&inner);
ipv4_udp([192, 168, 0, 1], [192, 168, 0, 2], 33333, 4789, &vxlan)
}
#[test]
fn vxlan_walk_finds_inner_layers() {
let f = vxlan_wrapped_frame();
let layers = Layers::parse_ethernet(&f).unwrap();
assert!(layers.has_tunnel(), "should detect tunnel");
let ipv4_count = layers.find_all(LayerKind::Ipv4).count();
assert_eq!(ipv4_count, 2, "outer + inner IPv4 expected");
let vx = layers.vxlan().expect("VXLAN slice");
assert_eq!(vx.vni(), 42);
let tcp = layers.tcp().expect("inner TCP");
assert_eq!(tcp.src_port(), 12345);
}
fn ipv4_in_ipv4_frame() -> Vec<u8> {
use etherparse::{EtherType, Ethernet2Header, IpNumber, Ipv4Header};
let inner = ipv4_tcp(
[0; 6],
[0; 6],
[10, 1, 0, 1],
[10, 1, 0, 2],
80,
443,
0,
0,
0x02,
b"hi",
);
let inner_ip_plus = &inner[14..];
let outer_ip = Ipv4Header::new(
inner_ip_plus.len() as u16,
64,
IpNumber(IP_PROTO_IPV4_OUTER),
[192, 168, 0, 1],
[192, 168, 0, 2],
)
.unwrap();
let eth = Ethernet2Header {
destination: [0; 6],
source: [0; 6],
ether_type: EtherType::IPV4,
};
let mut out = Vec::new();
eth.write(&mut out).unwrap();
outer_ip.write(&mut out).unwrap();
out.extend_from_slice(inner_ip_plus);
out
}
const IP_PROTO_IPV4_OUTER: u8 = 4;
#[test]
fn ipv4_in_ipv4_recognized_as_tunnel() {
let f = ipv4_in_ipv4_frame();
let layers = Layers::parse_ethernet(&f).unwrap();
assert!(layers.has_tunnel(), "should detect IP-in-IP tunnel");
let ipv4_count = layers.find_all(LayerKind::Ipv4).count();
assert_eq!(ipv4_count, 2);
}
#[test]
fn truncated_inner_keeps_outer_layers() {
let mut vxlan = vec![0x08u8, 0, 0, 0, 0, 0, 0x2a, 0];
vxlan.extend_from_slice(&[0, 0, 0, 0]); let f = ipv4_udp([192, 168, 0, 1], [192, 168, 0, 2], 33333, 4789, &vxlan);
let layers = Layers::parse_ethernet(&f).unwrap();
assert!(layers.ipv4().is_some());
assert!(layers.udp().is_some());
assert!(layers.vxlan().is_some());
assert!(layers.truncated());
}
fn icmp_echo_frame() -> Vec<u8> {
use etherparse::{EtherType, Ethernet2Header, IpNumber, Ipv4Header};
let icmp = vec![
8, 0, 0, 0, 0x12, 0x34, 0, 1, b'h', b'i',
];
let ip = Ipv4Header::new(
icmp.len() as u16,
64,
IpNumber(1), [10, 0, 0, 1],
[10, 0, 0, 2],
)
.unwrap();
let eth = Ethernet2Header {
destination: [0; 6],
source: [0; 6],
ether_type: EtherType::IPV4,
};
let mut out = Vec::new();
eth.write(&mut out).unwrap();
ip.write(&mut out).unwrap();
out.extend_from_slice(&icmp);
out
}
#[test]
fn icmpv4_slice_exposes_type_and_code() {
let f = icmp_echo_frame();
let layers = Layers::parse_ethernet(&f).unwrap();
let icmp = layers.icmpv4().expect("ICMPv4 slice");
assert_eq!(icmp.icmp_type(), 8);
assert_eq!(icmp.code(), 0);
assert!(matches!(layers.l4().unwrap(), Layer::Icmpv4(_)));
}