use crate::dissect::{Dissect, DissectResult};
use crate::Error;
#[derive(Debug, Clone)]
pub struct Ipv6<'a> {
data: &'a [u8],
}
impl<'a> Ipv6<'a> {
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
if data.len() < 40 {
return Err(Error::truncated(40, data.len()));
}
let version = data[0] >> 4;
if version != 6 {
return Err(Error::parse(
0,
format!("Invalid IPv6 version: {}", version),
));
}
Ok(Self { data })
}
pub fn version(&self) -> u8 {
self.data[0] >> 4
}
pub fn traffic_class(&self) -> u8 {
((self.data[0] & 0x0f) << 4) | (self.data[1] >> 4)
}
pub fn flow_label(&self) -> u32 {
u32::from_be_bytes([0, self.data[1] & 0x0f, self.data[2], self.data[3]])
}
pub fn payload_len(&self) -> u16 {
u16::from_be_bytes([self.data[4], self.data[5]])
}
pub fn next_header(&self) -> u8 {
self.data[6]
}
pub fn hop_limit(&self) -> u8 {
self.data[7]
}
pub fn src(&self) -> [u8; 16] {
let mut addr = [0u8; 16];
addr.copy_from_slice(&self.data[8..24]);
addr
}
pub fn dst(&self) -> [u8; 16] {
let mut addr = [0u8; 16];
addr.copy_from_slice(&self.data[24..40]);
addr
}
pub fn payload(&self) -> &'a [u8] {
&self.data[40..]
}
}
impl<'a> Dissect<'a> for Ipv6<'a> {
type Output = &'a [u8];
fn dissect(&self) -> DissectResult<Self::Output> {
Ok(self.payload())
}
fn data(&self) -> &'a [u8] {
self.data
}
fn header_len(&self) -> usize {
40
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ipv6_new_valid() {
let mut data = vec![0u8; 40];
data[0] = 0x60; data[4] = 0x00; data[6] = 6; data[7] = 64;
let ip = Ipv6::new(&data).unwrap();
assert_eq!(ip.version(), 6);
assert_eq!(ip.next_header(), 6);
}
#[test]
fn test_ipv6_new_truncated() {
let data = vec![0u8; 20];
let result = Ipv6::new(&data);
assert!(result.is_err());
}
#[test]
fn test_ipv6_new_invalid_version() {
let mut data = vec![0u8; 40];
data[0] = 0x40; let result = Ipv6::new(&data);
assert!(result.is_err());
}
#[test]
fn test_ipv6_addresses() {
let mut data = vec![0u8; 40];
data[0] = 0x60;
data[8] = 0x20;
data[9] = 0x01;
data[10] = 0x0d;
data[11] = 0xb8;
data[12] = 0x00;
data[13] = 0x00;
data[14] = 0x00;
data[15] = 0x00;
data[16] = 0x00;
data[17] = 0x00;
data[18] = 0x00;
data[19] = 0x00;
data[20] = 0x00;
data[21] = 0x00;
data[22] = 0x00;
data[23] = 0x01;
data[24] = 0x20;
data[25] = 0x01;
data[26] = 0x0d;
data[27] = 0xb8;
data[28] = 0x00;
data[29] = 0x00;
data[30] = 0x00;
data[31] = 0x00;
data[32] = 0x00;
data[33] = 0x00;
data[34] = 0x00;
data[35] = 0x02;
data[36] = 0x00;
data[37] = 0x00;
data[38] = 0x00;
data[39] = 0x00;
let ip = Ipv6::new(&data).unwrap();
assert_eq!(ip.src()[0], 0x20);
assert_eq!(ip.dst()[0], 0x20);
}
#[test]
fn test_ipv6_payload() {
let mut data = vec![0u8; 40]; data[0] = 0x60; data.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
let ip = Ipv6::new(&data).unwrap();
assert_eq!(ip.payload(), &[0xde, 0xad, 0xbe, 0xef]);
}
}