diff --git a/etherparse/src/net/ipv6_fragment_header.rs b/etherparse/src/net/ipv6_fragment_header.rs index 4ab7486d..06f6d7cd 100644 --- a/etherparse/src/net/ipv6_fragment_header.rs +++ b/etherparse/src/net/ipv6_fragment_header.rs @@ -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, } @@ -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. @@ -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. @@ -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], @@ -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], @@ -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); + } + } } diff --git a/etherparse/src/net/ipv6_fragment_header_slice.rs b/etherparse/src/net/ipv6_fragment_header_slice.rs index 00d85b32..01bdb272 100644 --- a/etherparse/src/net/ipv6_fragment_header_slice.rs +++ b/etherparse/src/net/ipv6_fragment_header_slice.rs @@ -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, + ) } } @@ -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 @@ -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()); @@ -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