#![cfg(feature = "extractors")]
use flowscope::layers::{Layer, LayerKind, Layers};
fn build_mpls_ipv4_frame() -> Vec<u8> {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]); f.extend_from_slice(&[0u8; 6]); f.extend_from_slice(&[0x88, 0x47]); f.extend_from_slice(&[0x00, 0x06, 0x41, 0x40]);
f.push(0x45); f.push(0x00); f.extend_from_slice(&[0x00, 40]); f.extend_from_slice(&[0; 2]); f.extend_from_slice(&[0; 2]); f.push(64); f.push(6); f.extend_from_slice(&[0; 2]); f.extend_from_slice(&[10, 0, 0, 1]); f.extend_from_slice(&[10, 0, 0, 2]); f.extend_from_slice(&[0xAB; 8]);
f
}
#[test]
fn mpls_inner_ipv4_is_reparsed() {
let f = build_mpls_ipv4_frame();
let layers = Layers::parse_ethernet(&f).expect("parse");
let kinds: Vec<LayerKind> = layers.iter().map(|l| l.kind()).collect();
assert!(
kinds.contains(&LayerKind::Mpls),
"MPLS layer missing: {kinds:?}"
);
assert!(
kinds.contains(&LayerKind::Ipv4),
"inner IPv4 NOT re-parsed (this is what issue #22 fixed): {kinds:?}"
);
}
fn build_mpls_ipv6_frame() -> Vec<u8> {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]); f.extend_from_slice(&[0u8; 6]); f.extend_from_slice(&[0x88, 0x47]); f.extend_from_slice(&[0x00, 0x06, 0x41, 0x40]); f.push(0x60); f.extend_from_slice(&[0x00, 0x00, 0x00]); f.extend_from_slice(&[0x00, 20]); f.push(6); f.push(64); f.extend_from_slice(&[0u8; 16]);
f.extend_from_slice(&[0u8; 16]);
f.extend_from_slice(&[0x00, 0x50, 0x00, 0x50]); f.extend_from_slice(&[0; 4]); f.extend_from_slice(&[0; 4]); f.push(0x50); f.push(0x02); f.extend_from_slice(&[0xFF; 2]); f.extend_from_slice(&[0; 2]); f.extend_from_slice(&[0; 2]); f
}
#[test]
fn mpls_inner_ipv6_is_reparsed() {
let f = build_mpls_ipv6_frame();
let layers = Layers::parse_ethernet(&f).expect("parse");
let kinds: Vec<LayerKind> = layers.iter().map(|l| l.kind()).collect();
assert!(
kinds.contains(&LayerKind::Mpls),
"MPLS layer missing: {kinds:?}"
);
assert!(
kinds.contains(&LayerKind::Ipv6),
"inner IPv6 NOT re-parsed: {kinds:?}"
);
}
#[test]
fn mpls_unknown_inner_returns_layers_without_l3() {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0x88, 0x47]);
f.extend_from_slice(&[0x00, 0x06, 0x41, 0x40]); f.extend_from_slice(&[0x00; 16]); let layers = Layers::parse_ethernet(&f).expect("parse");
let kinds: Vec<LayerKind> = layers.iter().map(|l| l.kind()).collect();
assert!(kinds.contains(&LayerKind::Mpls));
assert!(!kinds.contains(&LayerKind::Ipv4));
assert!(!kinds.contains(&LayerKind::Ipv6));
}
fn build_ipv6_tcp(next_header: u8) -> Vec<u8> {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0x86, 0xDD]); f.push(0x60); f.extend_from_slice(&[0x00, 0x00, 0x00]); f.extend_from_slice(&[0x00, 20]); f.push(next_header);
f.push(64); f.extend_from_slice(&[0u8; 16]); f.extend_from_slice(&[0u8; 16]); f.extend_from_slice(&[0x00, 0x50, 0x00, 0x50]);
f.extend_from_slice(&[0; 4]);
f.extend_from_slice(&[0; 4]);
f.push(0x50);
f.push(0x02);
f.extend_from_slice(&[0xFF; 2]);
f.extend_from_slice(&[0; 2]);
f.extend_from_slice(&[0; 2]);
f
}
#[test]
fn ipv6_no_extensions_walker_returns_immediate_next_header() {
let f = build_ipv6_tcp(6); let layers = Layers::parse_ethernet(&f).expect("parse");
let v6 = layers
.iter()
.find_map(|l| match l {
Layer::Ipv6(v6) => Some(v6),
_ => None,
})
.expect("v6 layer present");
let ext = v6.extensions();
assert_eq!(ext.upper_header, 6);
assert_eq!(ext.chain_depth, 0);
assert!(!ext.fragment_present);
assert!(!ext.chain_too_deep);
assert!(!ext.malformed);
assert_eq!(ext.payload_offset, 0);
}
fn build_ipv6_hbh_tcp() -> Vec<u8> {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0x86, 0xDD]);
f.push(0x60);
f.extend_from_slice(&[0x00, 0x00, 0x00]);
f.extend_from_slice(&[0x00, 28]); f.push(0); f.push(64);
f.extend_from_slice(&[0u8; 16]);
f.extend_from_slice(&[0u8; 16]);
f.push(6); f.push(0); f.extend_from_slice(&[0u8; 6]); f.extend_from_slice(&[0x00, 0x50, 0x00, 0x50]); f.extend_from_slice(&[0; 4]); f.extend_from_slice(&[0; 4]); f.push(0x50); f.push(0x02); f.extend_from_slice(&[0xFF; 2]); f.extend_from_slice(&[0; 2]); f.extend_from_slice(&[0; 2]); f
}
#[test]
fn ipv6_hop_by_hop_is_walked_to_tcp() {
let f = build_ipv6_hbh_tcp();
let layers = Layers::parse_ethernet(&f).expect("parse");
let v6 = layers
.iter()
.find_map(|l| match l {
Layer::Ipv6(v6) => Some(v6),
_ => None,
})
.expect("v6");
assert_eq!(v6.next_header(), 0, "raw next_header points at HBH");
let ext = v6.extensions();
assert_eq!(ext.upper_header, 6, "walked to TCP");
assert_eq!(ext.chain_depth, 1);
assert_eq!(ext.payload_offset, 8);
assert!(!ext.fragment_present);
assert!(!ext.malformed);
}
fn build_ipv6_fragment_tcp() -> Vec<u8> {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0x86, 0xDD]);
f.push(0x60);
f.extend_from_slice(&[0x00, 0x00, 0x00]);
f.extend_from_slice(&[0x00, 28]); f.push(44); f.push(64);
f.extend_from_slice(&[0u8; 16]);
f.extend_from_slice(&[0u8; 16]);
f.push(6); f.push(0); f.extend_from_slice(&[0x00, 0x00]); f.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); f.extend_from_slice(&[0x00, 0x50, 0x00, 0x50]); f.extend_from_slice(&[0; 4]); f.extend_from_slice(&[0; 4]); f.push(0x50); f.push(0x02); f.extend_from_slice(&[0xFF; 2]); f.extend_from_slice(&[0; 2]); f.extend_from_slice(&[0; 2]); f
}
#[test]
fn ipv6_fragment_header_flips_fragment_signal() {
let f = build_ipv6_fragment_tcp();
let layers = Layers::parse_ethernet(&f).expect("parse");
let v6 = layers
.iter()
.find_map(|l| match l {
Layer::Ipv6(v6) => Some(v6),
_ => None,
})
.expect("v6");
let ext = v6.extensions();
assert!(ext.fragment_present, "Fragment header should be detected");
assert_eq!(ext.upper_header, 6, "still walks to TCP");
assert_eq!(ext.chain_depth, 1);
assert!(layers.has_ipv6_fragment());
}
#[test]
fn layers_has_ipv6_fragment_false_when_no_extensions() {
let f = build_ipv6_tcp(6);
let layers = Layers::parse_ethernet(&f).expect("parse");
assert!(!layers.has_ipv6_fragment());
}
fn build_ipv6_deep_chain() -> Vec<u8> {
let mut f = Vec::new();
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0u8; 6]);
f.extend_from_slice(&[0x86, 0xDD]);
f.push(0x60);
f.extend_from_slice(&[0x00, 0x00, 0x00]);
let n_ext = 10usize; let payload_len = n_ext * 8 + 20; f.extend_from_slice(&[(payload_len >> 8) as u8, (payload_len & 0xff) as u8]);
f.push(0); f.push(64);
f.extend_from_slice(&[0u8; 16]);
f.extend_from_slice(&[0u8; 16]);
for i in 0..n_ext {
let next = if i + 1 == n_ext {
6 } else {
60 };
f.push(next);
f.push(0); f.extend_from_slice(&[0u8; 6]);
}
f.extend_from_slice(&[0x00, 0x50, 0x00, 0x50]); f.extend_from_slice(&[0; 4]); f.extend_from_slice(&[0; 4]); f.push(0x50); f.push(0x02); f.extend_from_slice(&[0xFF; 2]); f.extend_from_slice(&[0; 2]); f.extend_from_slice(&[0; 2]); f
}
#[test]
fn ipv6_chain_too_deep_evasion_signal() {
let f = build_ipv6_deep_chain();
let layers = Layers::parse_ethernet(&f).expect("parse");
let v6 = layers
.iter()
.find_map(|l| match l {
Layer::Ipv6(v6) => Some(v6),
_ => None,
})
.expect("v6");
let ext = v6.extensions();
assert!(
ext.chain_too_deep,
"deeply-chained ext headers should flip chain_too_deep"
);
assert_eq!(ext.chain_depth, 8, "walker stops at the 8-ext cap");
}