pub mod ext;
use std::fmt::{self, Formatter};
use std::net::Ipv6Addr;
use std::ops::Deref;
use zerocopy::byteorder::{BigEndian, U16};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use crate::packet::ipv6::ext::Ipv6ExtensionHeadersIter;
use crate::packet::protocol::IpProto;
use crate::packet::{HeaderParser, PacketHeader};
#[repr(C, packed)]
#[derive(FromBytes, IntoBytes, Immutable, Unaligned, KnownLayout, Debug, Clone, Copy)]
pub struct Ipv6Header {
ver_tc_flow: [u8; 4],
payload_length: U16<BigEndian>,
next_header: IpProto,
hop_limit: u8,
src_ip: [u8; 16],
dst_ip: [u8; 16],
}
pub const IPV6_NEXT_HOPBYHOP: IpProto = IpProto::IPV6_HOPOPT;
pub const IPV6_NEXT_TCP: IpProto = IpProto::TCP;
pub const IPV6_NEXT_UDP: IpProto = IpProto::UDP;
pub const IPV6_NEXT_IPV6: IpProto = IpProto::IPV6;
pub const IPV6_NEXT_ROUTING: IpProto = IpProto::IPV6_ROUTE;
pub const IPV6_NEXT_FRAGMENT: IpProto = IpProto::IPV6_FRAG;
pub const IPV6_NEXT_ICMPV6: IpProto = IpProto::IPV6_ICMP;
pub const IPV6_NEXT_NONE: IpProto = IpProto::IPV6_NONXT;
pub const IPV6_NEXT_DSTOPTS: IpProto = IpProto::IPV6_OPTS;
pub const IPV6_NEXT_MOBILITY: IpProto = IpProto::IPV6_MOBILITY;
impl Ipv6Header {
#[inline]
pub fn version(&self) -> u8 {
self.ver_tc_flow[0] >> 4
}
#[inline]
pub fn traffic_class(&self) -> u8 {
((self.ver_tc_flow[0] & 0x0F) << 4) | (self.ver_tc_flow[1] >> 4)
}
#[inline]
pub fn dscp(&self) -> u8 {
self.traffic_class() >> 2
}
#[inline]
pub fn ecn(&self) -> u8 {
self.traffic_class() & 0x03
}
#[inline]
pub fn flow_label(&self) -> u32 {
let b1 = (self.ver_tc_flow[1] & 0x0F) as u32;
let b2 = self.ver_tc_flow[2] as u32;
let b3 = self.ver_tc_flow[3] as u32;
(b1 << 16) | (b2 << 8) | b3
}
#[inline]
pub fn payload_length(&self) -> usize {
self.payload_length.get() as usize
}
#[inline]
pub fn total_length(&self) -> usize {
Self::FIXED_LEN + self.payload_length()
}
#[inline]
pub fn next_header(&self) -> IpProto {
self.next_header
}
#[inline]
pub fn hop_limit(&self) -> u8 {
self.hop_limit
}
#[inline]
pub fn src_ip(&self) -> Ipv6Addr {
Ipv6Addr::from(self.src_ip)
}
#[inline]
pub fn dst_ip(&self) -> Ipv6Addr {
Ipv6Addr::from(self.dst_ip)
}
#[inline]
pub fn src_ip_raw(&self) -> [u8; 16] {
self.src_ip
}
#[inline]
pub fn dst_ip_raw(&self) -> [u8; 16] {
self.dst_ip
}
#[inline]
pub fn has_extension_headers(&self) -> bool {
matches!(
self.next_header,
IPV6_NEXT_HOPBYHOP | IPV6_NEXT_ROUTING | IPV6_NEXT_FRAGMENT | IPV6_NEXT_DSTOPTS
)
}
#[inline]
pub fn is_transport_protocol(&self) -> bool {
matches!(self.next_header, IPV6_NEXT_TCP | IPV6_NEXT_UDP)
}
fn parse_extension_headers(
buf: &[u8],
bytes_available: usize,
initial_next_header: IpProto,
allow_fragment: bool,
) -> Option<(usize, IpProto, bool)> {
if bytes_available < Self::FIXED_LEN {
return None;
}
let mut len = Self::FIXED_LEN;
let mut next_hdr = initial_next_header;
let mut is_fragmented = false;
while matches!(
next_hdr,
IPV6_NEXT_HOPBYHOP
| IPV6_NEXT_ROUTING
| IPV6_NEXT_DSTOPTS
| IPV6_NEXT_MOBILITY
| IPV6_NEXT_FRAGMENT
) {
if next_hdr == IPV6_NEXT_FRAGMENT {
if !allow_fragment {
return None;
}
is_fragmented = true;
if len >= bytes_available {
return None;
}
next_hdr = IpProto::from(buf[len]);
len += 8;
} else {
if len + 2 > bytes_available {
return None;
}
next_hdr = IpProto::from(buf[len]);
let ext_len = buf[len + 1];
let delta = (1 + ext_len as usize) * 8;
if delta == 0 {
return None;
}
len += delta;
if len > bytes_available {
return None;
}
}
}
Some((len, next_hdr, is_fragmented))
}
pub fn total_header_len(&self, buf: &[u8], bytes_available: usize) -> usize {
Self::parse_extension_headers(buf, bytes_available, self.next_header, false)
.map(|(len, _, _)| len)
.unwrap_or(0)
}
pub fn upper_layer_protocol(
&self,
buf: &[u8],
bytes_available: usize,
) -> Option<(IpProto, bool)> {
Self::parse_extension_headers(buf, bytes_available, self.next_header, true)
.map(|(_, protocol, is_fragmented)| (protocol, is_fragmented))
}
#[inline]
pub fn should_parse_extensions(&self) -> bool {
self.has_extension_headers()
}
}
#[derive(Debug, Clone)]
pub struct Ipv6HeaderExt<'a> {
pub header: &'a Ipv6Header,
pub raw_extensions: &'a [u8],
}
impl<'a> Ipv6HeaderExt<'a> {
pub fn extensions(&'a self) -> Ipv6ExtensionHeadersIter<'a> {
Ipv6ExtensionHeadersIter::new(self.header.next_header, self.raw_extensions)
}
pub fn ext_headers_len(&self) -> usize {
Ipv6Header::FIXED_LEN + self.raw_extensions.len()
}
}
impl Deref for Ipv6HeaderExt<'_> {
type Target = Ipv6Header;
#[inline]
fn deref(&self) -> &Self::Target {
self.header
}
}
impl PacketHeader for Ipv6Header {
const NAME: &'static str = "IPv6Header";
type InnerType = IpProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
self.next_header
}
#[inline]
fn total_len(&self, buf: &[u8]) -> usize {
Self::parse_extension_headers(buf, buf.len(), self.next_header, true)
.map(|(len, _, _)| len)
.unwrap_or(Self::FIXED_LEN) }
#[inline]
fn is_valid(&self) -> bool {
self.version() == 6
}
}
impl HeaderParser for Ipv6Header {
type Output<'a> = Ipv6HeaderExt<'a>;
#[inline]
fn into_view<'a>(header: &'a Self, raw_extensions: &'a [u8]) -> Self::Output<'a> {
Ipv6HeaderExt {
header,
raw_extensions,
}
}
}
impl fmt::Display for Ipv6Header {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"IPv6 {} -> {} proto={} hop={} len={}",
self.src_ip(),
self.dst_ip(),
self.next_header(),
self.hop_limit(),
self.total_length()
)?;
if self.flow_label() != 0 {
write!(f, " flow=0x{:05x}", self.flow_label())?;
}
if self.has_extension_headers() {
write!(f, " +exts")?;
}
Ok(())
}
}
impl fmt::Display for Ipv6HeaderExt<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.header)?;
if !self.raw_extensions.is_empty() {
write!(f, " exts=[")?;
let mut first = true;
for ext in self.extensions().flatten() {
if !first {
write!(f, ",")?;
}
first = false;
write!(f, "{}", ext)?;
}
write!(f, "]")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
#[test]
fn test_ipv6_header_size() {
assert_eq!(mem::size_of::<Ipv6Header>(), 40);
assert_eq!(Ipv6Header::FIXED_LEN, 40);
}
#[test]
fn test_ipv6_version() {
let header = create_test_header();
assert_eq!(header.version(), 6);
assert!(header.is_valid());
}
#[test]
fn test_ipv6_traffic_class() {
let mut header = create_test_header();
header.ver_tc_flow[0] = 0x6A;
header.ver_tc_flow[1] = 0xB0;
assert_eq!(header.traffic_class(), 0xAB);
assert_eq!(header.dscp(), 0xAB >> 2);
assert_eq!(header.ecn(), 0xAB & 0x03);
}
#[test]
fn test_ipv6_flow_label() {
let mut header = create_test_header();
header.ver_tc_flow[1] = (header.ver_tc_flow[1] & 0xF0) | 0x01;
header.ver_tc_flow[2] = 0x23;
header.ver_tc_flow[3] = 0x45;
assert_eq!(header.flow_label(), 0x12345);
}
#[test]
fn test_ipv6_addresses() {
let header = create_test_header();
let expected_src = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1);
assert_eq!(header.src_ip(), expected_src);
let expected_dst = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 2);
assert_eq!(header.dst_ip(), expected_dst);
}
#[test]
fn test_ipv6_payload_length() {
let mut header = create_test_header();
header.payload_length = U16::new(1024);
assert_eq!(header.payload_length(), 1024);
assert_eq!(header.total_length(), 40 + 1024);
}
#[test]
fn test_ipv6_next_header() {
let mut header = create_test_header();
header.next_header = IPV6_NEXT_TCP;
assert_eq!(header.next_header(), IpProto::TCP);
assert!(header.is_transport_protocol());
assert!(!header.has_extension_headers());
header.next_header = IPV6_NEXT_FRAGMENT;
assert!(header.has_extension_headers());
assert!(!header.is_transport_protocol());
}
#[test]
fn test_ipv6_parsing() {
let packet = create_test_packet();
let result = Ipv6Header::from_bytes(&packet);
assert!(result.is_ok());
let (header_ext, payload) = result.unwrap();
assert_eq!(header_ext.version(), 6);
assert_eq!(header_ext.next_header(), IPV6_NEXT_TCP);
assert_eq!(header_ext.hop_limit(), 64);
assert_eq!(header_ext.raw_extensions.len(), 0);
assert_eq!(payload.len(), 0);
}
#[test]
fn test_ipv6_parsing_invalid_version() {
let mut packet = create_test_packet();
packet[0] = 0x40;
let result = Ipv6Header::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_ipv6_parsing_too_small() {
let packet = vec![0u8; 39];
let result = Ipv6Header::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_ipv6_total_header_len_no_extensions() {
let packet = create_test_packet();
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header.total_header_len(&packet, packet.len()), 40);
}
#[test]
fn test_ipv6_total_header_len_with_extensions() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_TCP.into());
packet.push(0);
packet.extend_from_slice(&[0, 0, 0, 0, 0, 0]);
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header.total_header_len(&packet, packet.len()), 48);
}
#[test]
fn test_ipv6_upper_layer_protocol() {
let packet = create_test_packet();
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
let result = header.upper_layer_protocol(&packet, packet.len());
assert_eq!(result, Some((IpProto::TCP, false)));
}
#[test]
fn test_ipv6_upper_layer_protocol_with_extensions() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_TCP.into()); packet.push(0); packet.extend_from_slice(&[0, 0, 0, 0, 0, 0]);
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
let result = header.upper_layer_protocol(&packet, packet.len());
assert_eq!(result, Some((IpProto::TCP, false)));
}
#[test]
fn test_ipv6_from_bytes_with_routing_extension() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_ROUTING.into();
packet.push(IPV6_NEXT_TCP.into()); packet.push(2); packet.push(0); packet.push(1); packet.extend_from_slice(&[0, 0, 0, 0]);
packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03,
]);
let payload_data = b"Test payload after routing header";
packet.extend_from_slice(payload_data);
let total_payload = 24 + payload_data.len();
packet[4] = ((total_payload >> 8) & 0xFF) as u8;
packet[5] = (total_payload & 0xFF) as u8;
let result = Ipv6Header::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert_eq!(payload.len(), payload_data.len());
assert_eq!(payload, payload_data);
assert_eq!(header.next_header(), IpProto::IPV6_ROUTE);
assert!(header.has_extension_headers());
assert_eq!(header.total_length(), 40 + 24 + payload_data.len());
assert_eq!(header.payload_length(), 24 + payload_data.len());
let total_header_len = header.total_header_len(&packet, packet.len());
assert!(header.total_length() >= total_header_len);
}
#[test]
fn test_ipv6_from_bytes_with_hopbyhop_extension() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_UDP.into()); packet.push(0); packet.push(1); packet.push(4); packet.extend_from_slice(&[0, 0, 0, 0]);
let payload_data = b"UDP payload";
packet.extend_from_slice(payload_data);
let total_payload = 8 + payload_data.len();
packet[4] = ((total_payload >> 8) & 0xFF) as u8;
packet[5] = (total_payload & 0xFF) as u8;
let result = Ipv6Header::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert_eq!(payload.len(), payload_data.len());
assert_eq!(payload, payload_data);
assert_eq!(header.next_header(), IpProto::IPV6_HOPOPT);
assert!(header.has_extension_headers());
assert_eq!(header.total_length(), 40 + 8 + payload_data.len());
assert_eq!(header.payload_length(), 8 + payload_data.len());
let total_header_len = header.total_header_len(&packet, packet.len());
assert!(header.total_length() >= total_header_len);
}
#[test]
fn test_ipv6_multiple_chained_extension_headers() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_ROUTING.into()); packet.push(0); packet.push(1); packet.push(4); packet.extend_from_slice(&[0, 0, 0, 0]);
packet.push(IPV6_NEXT_DSTOPTS.into()); packet.push(2); packet.push(0); packet.push(1); packet.extend_from_slice(&[0, 0, 0, 0]); packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03,
]);
packet.push(IPV6_NEXT_TCP.into()); packet.push(0); packet.push(1); packet.push(4); packet.extend_from_slice(&[0, 0, 0, 0]);
let payload_data = b"TCP payload after multiple extension headers";
packet.extend_from_slice(payload_data);
let total_payload = 8 + 24 + 8 + payload_data.len();
packet[4] = ((total_payload >> 8) & 0xFF) as u8;
packet[5] = (total_payload & 0xFF) as u8;
let result = Ipv6Header::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert_eq!(payload.len(), payload_data.len());
assert_eq!(payload, payload_data);
assert_eq!(header.next_header(), IpProto::IPV6_HOPOPT);
assert!(header.has_extension_headers());
assert_eq!(header.total_header_len(&packet, packet.len()), 80);
let result = header.upper_layer_protocol(&packet, packet.len());
assert_eq!(result, Some((IpProto::TCP, false)));
assert_eq!(header.total_length(), 40 + 8 + 24 + 8 + payload_data.len());
assert_eq!(header.payload_length(), 8 + 24 + 8 + payload_data.len());
let total_header_len = header.total_header_len(&packet, packet.len());
assert!(header.total_length() >= total_header_len);
assert_eq!(header.total_length(), total_header_len + payload_data.len());
}
#[test]
fn test_ipv6_routing_extension_header() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_ROUTING.into();
packet.push(IPV6_NEXT_TCP.into()); packet.push(2); packet.push(0); packet.push(1); packet.extend_from_slice(&[0, 0, 0, 0]);
packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03,
]);
packet[4] = 0;
packet[5] = 24;
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert!(header.has_extension_headers());
assert!(!header.is_transport_protocol());
assert_eq!(header.total_header_len(&packet, packet.len()), 64);
let result = header.upper_layer_protocol(&packet, packet.len());
assert_eq!(result, Some((IpProto::TCP, false)));
let total_header_len = header.total_header_len(&packet, packet.len());
assert!(header.total_length() >= total_header_len);
assert_eq!(header.total_length(), 40 + 24); assert_eq!(header.payload_length(), 24);
}
#[test]
fn test_ipv6_hopbyhop_extension_header() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_UDP.into()); packet.push(0);
packet.push(1); packet.push(4); packet.extend_from_slice(&[0, 0, 0, 0]);
packet[4] = 0;
packet[5] = 8;
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert!(header.has_extension_headers());
assert!(!header.is_transport_protocol());
assert_eq!(header.total_header_len(&packet, packet.len()), 48);
let result = header.upper_layer_protocol(&packet, packet.len());
assert_eq!(result, Some((IpProto::UDP, false)));
let total_header_len = header.total_header_len(&packet, packet.len());
assert!(header.total_length() >= total_header_len);
assert_eq!(header.total_length(), 40 + 8); assert_eq!(header.payload_length(), 8);
}
#[test]
fn test_ipv6_total_len_includes_extension_headers() {
let mut packet = create_test_packet();
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 40);
packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_TCP.into());
packet.push(0);
packet.extend_from_slice(&[1, 4, 0, 0, 0, 0]);
packet[4] = 0;
packet[5] = 8;
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 48);
packet = create_test_packet();
packet[6] = IPV6_NEXT_ROUTING.into();
packet.push(IPV6_NEXT_TCP.into());
packet.push(2);
packet.push(0);
packet.push(1);
packet.extend_from_slice(&[0, 0, 0, 0]);
packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03,
]);
packet[4] = 0;
packet[5] = 24;
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 64);
packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet.push(IPV6_NEXT_ROUTING.into());
packet.push(0);
packet.extend_from_slice(&[1, 4, 0, 0, 0, 0]);
packet.push(IPV6_NEXT_TCP.into());
packet.push(2);
packet.push(0);
packet.push(1);
packet.extend_from_slice(&[0, 0, 0, 0]);
packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03,
]);
packet[4] = 0;
packet[5] = 32;
let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 72); }
fn create_test_header() -> Ipv6Header {
Ipv6Header {
ver_tc_flow: [0x60, 0x00, 0x00, 0x00], payload_length: U16::new(0),
next_header: IpProto::TCP,
hop_limit: 64,
src_ip: [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
dst_ip: [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
}
}
fn create_test_packet() -> Vec<u8> {
let mut packet = Vec::new();
packet.extend_from_slice(&[0x60, 0x00, 0x00, 0x00]);
packet.extend_from_slice(&[0x00, 0x00]);
packet.push(IpProto::TCP.into());
packet.push(64);
packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]);
packet.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
]);
packet
}
#[test]
fn test_ipv6_header_ext_no_extensions() {
let packet = create_test_packet();
let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_extensions.len(), 0);
assert!(!header_ext.should_parse_extensions());
assert_eq!(header_ext.ext_headers_len(), 40);
assert_eq!(header_ext.version(), 6);
assert_eq!(header_ext.next_header(), IPV6_NEXT_TCP);
assert_eq!(header_ext.hop_limit(), 64);
}
#[test]
fn test_ipv6_header_ext_with_fragment() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_FRAGMENT.into();
packet[4] = 0;
packet[5] = 8;
packet.extend_from_slice(&[
IpProto::TCP.into(), 0, 0x00,
0x01, 0x00,
0x00,
0x00,
0x01, ]);
let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_extensions.len(), 8);
assert!(header_ext.should_parse_extensions());
assert_eq!(header_ext.ext_headers_len(), 48);
let exts: Vec<_> = header_ext
.extensions()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(exts.len(), 1);
use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
match &exts[0] {
Ipv6ExtensionHeader::Fragment {
next_header,
fragment_offset,
more_fragments,
identification,
} => {
assert_eq!(*next_header, IpProto::TCP);
assert_eq!(*fragment_offset, 0);
assert!(*more_fragments);
assert_eq!(*identification, 1);
}
_ => panic!("Expected Fragment header"),
}
}
#[test]
fn test_ipv6_header_ext_with_hop_by_hop() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet[4] = 0;
packet[5] = 8;
packet.extend_from_slice(&[
IpProto::TCP.into(), 0, 1,
4,
0,
0,
0,
0, ]);
let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_extensions.len(), 8);
assert_eq!(header_ext.ext_headers_len(), 48);
let exts: Vec<_> = header_ext
.extensions()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(exts.len(), 1);
use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
match &exts[0] {
Ipv6ExtensionHeader::HopByHop {
next_header,
options,
} => {
assert_eq!(*next_header, IpProto::TCP);
assert_eq!(options.len(), 6);
}
_ => panic!("Expected HopByHop header"),
}
}
#[test]
fn test_ipv6_header_ext_multiple_extensions() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_HOPBYHOP.into();
packet[4] = 0;
packet[5] = 16;
packet.extend_from_slice(&[
IPV6_NEXT_FRAGMENT.into(), 0, 1,
4,
0,
0,
0,
0, ]);
packet.extend_from_slice(&[
IpProto::TCP.into(), 0, 0x00,
0x00, 0x00,
0x00,
0x00,
0x42, ]);
let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_extensions.len(), 16);
assert_eq!(header_ext.ext_headers_len(), 56);
let exts: Vec<_> = header_ext
.extensions()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(exts.len(), 2);
use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
assert!(matches!(&exts[0], Ipv6ExtensionHeader::HopByHop { .. }));
assert!(matches!(&exts[1], Ipv6ExtensionHeader::Fragment { .. }));
assert_eq!(exts[0].next_header(), IPV6_NEXT_FRAGMENT);
assert_eq!(exts[1].next_header(), IpProto::TCP);
}
#[test]
fn test_ipv6_header_ext_deref() {
let packet = create_test_packet();
let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.version(), 6);
assert_eq!(header_ext.traffic_class(), 0);
assert_eq!(header_ext.flow_label(), 0);
assert_eq!(header_ext.payload_length(), 0);
assert_eq!(header_ext.next_header(), IPV6_NEXT_TCP);
assert_eq!(header_ext.hop_limit(), 64);
let src = header_ext.src_ip();
assert_eq!(src.to_string(), "2001:db8::1");
let dst = header_ext.dst_ip();
assert_eq!(dst.to_string(), "2001:db8::2");
}
#[test]
fn test_ipv6_header_ext_with_routing() {
let mut packet = create_test_packet();
packet[6] = IPV6_NEXT_ROUTING.into();
packet[4] = 0;
packet[5] = 8;
packet.extend_from_slice(&[
IpProto::TCP.into(), 0, 0, 0, 0,
0,
0,
0, ]);
let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_extensions.len(), 8);
let exts: Vec<_> = header_ext
.extensions()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(exts.len(), 1);
use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
match &exts[0] {
Ipv6ExtensionHeader::Routing {
next_header,
routing_type,
segments_left,
data,
} => {
assert_eq!(*next_header, IpProto::TCP);
assert_eq!(*routing_type, 0);
assert_eq!(*segments_left, 0);
assert_eq!(data.len(), 4);
}
_ => panic!("Expected Routing header"),
}
}
}