use alloc::string::String;
use core::net::Ipv6Addr;
use crate::reverse::{be_u16, be_u32, ipv6_at, push_hex, push_if};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Ipv6HeaderFields<'a> {
pub(crate) src_ip: Ipv6Addr,
pub(crate) dst_ip: Ipv6Addr,
pub(crate) next_header: u8,
pub(crate) ttl: u8,
pub(crate) dscp: u8,
pub(crate) ecn: u8,
pub(crate) flowlabel: u32,
pub(crate) payload: &'a [u8],
}
impl<'a> Ipv6HeaderFields<'a> {
pub(crate) const fn hop_limit(&self) -> u8 {
self.ttl
}
pub(crate) fn append_fasm_modifiers(&self, out: &mut String, default_ttl: u8) {
push_if(out, "ttl", self.ttl, default_ttl);
push_if(out, "dscp", self.dscp, 0);
push_if(out, "ecn", self.ecn, 0);
push_hex(out, "flowlabel", u64::from(self.flowlabel), 0, 1);
}
pub(crate) fn is_zero_overlay_stub(&self) -> bool {
if !self.src_ip.is_unspecified()
|| !self.dst_ip.is_unspecified()
|| self.next_header != 0
|| self.hop_limit() != 0
|| self.dscp != 0
|| self.ecn != 0
|| self.flowlabel != 0
{
return false;
}
let mut mods = String::new();
self.append_fasm_modifiers(&mut mods, 0);
mods.is_empty()
}
}
pub(crate) fn extract_ipv6_l3(b: &[u8]) -> Option<Ipv6HeaderFields<'_>> {
if b.len() < 40 || (b[0] >> 4) != 6 {
return None;
}
let ver_tc_fl = be_u32(b, 0);
let tc = ((ver_tc_fl >> 20) & 0xFF) as u8;
let payload_len = be_u16(b, 4) as usize;
let pkt_end = if 40 + payload_len <= b.len() {
40 + payload_len
} else {
b.len()
};
let mut next_header = b[6];
let mut pos = 40usize;
while pos < pkt_end {
if matches!(next_header, 0 | 43 | 60) {
if pos + 2 > pkt_end {
break;
}
let ext_len = ((b[pos + 1] as usize) + 1) * 8;
if pos + ext_len > pkt_end {
break;
}
next_header = b[pos];
pos += ext_len;
} else if next_header == 44 {
if pos + 8 > pkt_end {
break;
}
next_header = b[pos];
pos += 8;
} else {
break;
}
}
Some(Ipv6HeaderFields {
src_ip: ipv6_at(b, 8),
dst_ip: ipv6_at(b, 24),
next_header,
ttl: b[7],
dscp: tc >> 2,
ecn: tc & 0x03,
flowlabel: ver_tc_fl & 0x000F_FFFF,
payload: &b[pos..pkt_end],
})
}