Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 56 additions & 48 deletions etherparse/src/net/ipv6_fragment_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct Ipv6FragmentHeader {
pub fragment_offset: IpFragOffset,
/// True if more fragment packets will follow. False if this is the last packet.
pub more_fragments: bool,
/// Identifcation value generated by the source.
/// Identification value generated by the source.
pub identification: u32,
}

Expand Down Expand Up @@ -57,19 +57,9 @@ impl Ipv6FragmentHeader {
buffer
};

Ok(Ipv6FragmentHeader {
next_header: IpNumber(buffer[0]),
fragment_offset: unsafe {
// SAFE as the resulting number is guaranteed to have at most
// 13 bits.
IpFragOffset::new_unchecked(u16::from_be_bytes([
(buffer[2] >> 3) & 0b0001_1111u8,
((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8),
]))
},
more_fragments: 0 != buffer[3] & 0b1000_0000u8,
identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
})
// SAFETY: `buffer` is exactly 8 bytes long, satisfying the length
// requirement of `from_slice_unchecked`.
Ok(unsafe { Ipv6FragmentHeaderSlice::from_slice_unchecked(&buffer) }.to_header())
}

/// Read an fragment header from the current reader position.
Expand All @@ -89,19 +79,9 @@ impl Ipv6FragmentHeader {
buffer
};

Ok(Ipv6FragmentHeader {
next_header: IpNumber(buffer[0]),
fragment_offset: unsafe {
// SAFE as the resulting number is guaranteed to have at most
// 13 bits.
IpFragOffset::new_unchecked(u16::from_be_bytes([
(buffer[2] >> 3) & 0b0001_1111u8,
((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8),
]))
},
more_fragments: 0 != buffer[3] & 0b1000_0000u8,
identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
})
// SAFETY: `buffer` is exactly 8 bytes long, satisfying the length
// requirement of `from_slice_unchecked`.
Ok(unsafe { Ipv6FragmentHeaderSlice::from_slice_unchecked(&buffer) }.to_header())
}

/// Writes a given IPv6 fragment header to the current position.
Expand Down Expand Up @@ -168,18 +148,17 @@ impl Ipv6FragmentHeader {
/// sized byte array.
#[inline]
pub fn to_bytes(&self) -> [u8; 8] {
let fo_be: [u8; 2] = self.fragment_offset.value().to_be_bytes();
// fragment offset is the upper 13 bits, the more fragments flag is
// the least significant bit (bits 2..1 are reserved & left zero).
let fo_be: [u8; 2] = ((self.fragment_offset.value() << 3)
| if self.more_fragments { 1 } else { 0 })
.to_be_bytes();
let id_be = self.identification.to_be_bytes();
[
self.next_header.0,
0,
(((fo_be[0] << 3) & 0b1111_1000u8) | ((fo_be[1] >> 5) & 0b0000_0111u8)),
((fo_be[1] & 0b0001_1111u8)
| if self.more_fragments {
0b1000_0000u8
} else {
0
}),
fo_be[0],
fo_be[1],
id_be[0],
id_be[1],
id_be[2],
Expand Down Expand Up @@ -397,25 +376,18 @@ mod test {

// normal write
{
let fragment_offset_be = input.fragment_offset.value().to_be_bytes();
let fragment_offset_be = (
(input.fragment_offset.value() << 3) |
if input.more_fragments { 1u16 } else { 0u16 }
).to_be_bytes();
let id_be = input.identification.to_be_bytes();
assert_eq!(
&input.to_bytes(),
&[
input.next_header.0,
0,
(
(fragment_offset_be[0] << 3 & 0b1111_1000u8) |
(fragment_offset_be[1] >> 5 & 0b0000_0111u8)
),
(
(fragment_offset_be[1] & 0b0001_1111u8) |
if input.more_fragments {
0b1000_0000u8
} else {
0u8
}
),
fragment_offset_be[0],
fragment_offset_be[1],
id_be[0],
id_be[1],
id_be[2],
Expand All @@ -425,4 +397,40 @@ mod test {
}
}
}

/// Verify the exact RFC 8200 §4.5 wire layout: the 13 bit fragment offset
/// occupies the most significant bits of bytes 2..3 and the "more
/// fragments" flag is the least significant bit of byte 3.
#[test]
fn network_byte_order() {
use crate::ip_number::UDP;

// max offset (8191, all 13 bits set) with the more fragments flag
// set. The offset fills bytes 2..3 bits 15..3 and M is bit 0 (bits
// 2..1 are reserved & zero).
{
let header = Ipv6FragmentHeader::new(UDP, IpFragOffset::try_new(8191).unwrap(), true, 0);
let bytes = [UDP.0, 0, 0b1111_1111, 0b1111_1001, 0, 0, 0, 0];
assert_eq!(header.to_bytes(), bytes);
assert_eq!(Ipv6FragmentHeader::from_slice(&bytes).unwrap().0, header);
}

// max offset (8191, all 13 bits set) without the more fragments flag.
{
let header =
Ipv6FragmentHeader::new(UDP, IpFragOffset::try_new(8191).unwrap(), false, 0);
let bytes = [UDP.0, 0, 0b1111_1111, 0b1111_1000, 0, 0, 0, 0];
assert_eq!(header.to_bytes(), bytes);
assert_eq!(Ipv6FragmentHeader::from_slice(&bytes).unwrap().0, header);
}

// offset 1 with more fragments -> low byte holds the offset's least
// significant bit (bit 3) and M (bit 0).
{
let header = Ipv6FragmentHeader::new(UDP, IpFragOffset::try_new(1).unwrap(), true, 0);
let bytes = [UDP.0, 0, 0b0000_0000, 0b0000_1001, 0, 0, 0, 0];
assert_eq!(header.to_bytes(), bytes);
assert_eq!(Ipv6FragmentHeader::from_slice(&bytes).unwrap().0, header);
}
}
}
24 changes: 10 additions & 14 deletions etherparse/src/net/ipv6_fragment_header_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ impl<'a> Ipv6FragmentHeaderSlice<'a> {
pub fn fragment_offset(&self) -> IpFragOffset {
unsafe {
// SAFETY: Safe as the resulting number is guaranteed to be only
// 13 bit long.
IpFragOffset::new_unchecked(u16::from_be_bytes([
// 13 bit long (the lower 3 bits are shifted away).
IpFragOffset::new_unchecked(
// SAFETY:
// Slice size checked to be at least 8 bytes in constructor.
(*self.slice.get_unchecked(2) >> 3) & 0b0001_1111u8,
((*self.slice.get_unchecked(2) << 5) & 0b1110_0000u8)
| (*self.slice.get_unchecked(3) & 0b0001_1111u8),
]))
u16::from_be_bytes([*self.slice.get_unchecked(2), *self.slice.get_unchecked(3)])
>> 3,
)
}
}

Expand All @@ -82,7 +81,7 @@ impl<'a> Ipv6FragmentHeaderSlice<'a> {
pub fn more_fragments(&self) -> bool {
// SAFETY:
// Slice size checked to be at least 8 bytes in constructor.
unsafe { 0 != *self.slice.get_unchecked(3) & 0b1000_0000u8 }
unsafe { 0 != *self.slice.get_unchecked(3) & 0b0000_0001u8 }
}

/// Identifcation value generated by the source
Expand Down Expand Up @@ -126,7 +125,7 @@ impl<'a> Ipv6FragmentHeaderSlice<'a> {
///
/// {
/// let slice = Ipv6FragmentHeaderSlice::from_slice(&[
/// 0, 0, 0, 0b1000_0000u8, // more_fragments set
/// 0, 0, 0, 0b0000_0001u8, // more_fragments set
/// 1, 2, 3, 4,
/// ]).unwrap();
/// assert!(slice.is_fragmenting_payload());
Expand All @@ -142,12 +141,9 @@ impl<'a> Ipv6FragmentHeaderSlice<'a> {
/// ```
#[inline]
pub fn is_fragmenting_payload(&self) -> bool {
// SAFETY:
// Slice size checked to be at least 8 bytes in constructor.
unsafe {
0 != *self.slice.get_unchecked(2) || 0 != (*self.slice.get_unchecked(3) & 0b1001_1111u8)
// exclude the reserved bytes
}
// decode via the accessors so the wire layout lives in one place
// (the reserved bits are inherently ignored as a result)
self.more_fragments() || 0 != self.fragment_offset().value()
}

/// Decode some of the fields and copy the results to a
Expand Down
Loading