1 use crate::*; 2 use core::slice::from_raw_parts; 3 4 /// Slice containing an IPv6 fragment header. 5 #[derive(Clone, Debug, Eq, PartialEq)] 6 pub struct Ipv6FragmentHeaderSlice<'a> { 7 /// Slice containing the packet data. 8 slice: &'a [u8], 9 } 10 11 impl<'a> Ipv6FragmentHeaderSlice<'a> { 12 /// Creates a hop by hop header slice from a slice. from_slice(slice: &'a [u8]) -> Result<Ipv6FragmentHeaderSlice<'a>, err::LenError>13 pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6FragmentHeaderSlice<'a>, err::LenError> { 14 // the fragmentation header has the exact size of 8 bytes 15 if slice.len() < 8 { 16 Err(err::LenError { 17 required_len: 8, 18 len: slice.len(), 19 len_source: LenSource::Slice, 20 layer: err::Layer::Ipv6FragHeader, 21 layer_start_offset: 0, 22 }) 23 } else { 24 Ok(Ipv6FragmentHeaderSlice { 25 // SAFETY: 26 // Safe as slice length is checked to be at least 8 before this 27 // code can be reached. 28 slice: unsafe { from_raw_parts(slice.as_ptr(), 8) }, 29 }) 30 } 31 } 32 33 /// Creates a hop by hop header slice from a slice (assumes slice size & content was validated before). 34 /// 35 /// # Safety 36 /// 37 /// This function assumes that the passed slice has at least the length 38 /// of 8. If a slice with length less then 8 is passed to this function 39 /// the behavior will be undefined. from_slice_unchecked(slice: &'a [u8]) -> Ipv6FragmentHeaderSlice<'a>40 pub unsafe fn from_slice_unchecked(slice: &'a [u8]) -> Ipv6FragmentHeaderSlice<'a> { 41 debug_assert!(slice.len() >= Ipv6FragmentHeader::LEN); 42 // the fragmentation header has the exact size of 8 bytes 43 Ipv6FragmentHeaderSlice { 44 slice: from_raw_parts(slice.as_ptr(), Ipv6FragmentHeader::LEN), 45 } 46 } 47 48 /// Returns the slice containing the ipv6 fragment header. 49 #[inline] slice(&self) -> &'a [u8]50 pub fn slice(&self) -> &'a [u8] { 51 self.slice 52 } 53 54 /// Returns the IP protocol number of the next header. 55 /// 56 /// See [IpNumber] or [ip_number] for a definition of the known values. 57 #[inline] next_header(&self) -> IpNumber58 pub fn next_header(&self) -> IpNumber { 59 // SAFETY: 60 // Slice size checked to be at least 8 bytes in constructor. 61 IpNumber(unsafe { *self.slice.get_unchecked(0) }) 62 } 63 64 /// Fragment offset 65 #[inline] fragment_offset(&self) -> IpFragOffset66 pub fn fragment_offset(&self) -> IpFragOffset { 67 unsafe { 68 // SAFETY: Safe as the resulting number is guaranteed to be only 69 // 13 bit long. 70 IpFragOffset::new_unchecked(u16::from_be_bytes([ 71 // SAFETY: 72 // Slice size checked to be at least 8 bytes in constructor. 73 (*self.slice.get_unchecked(2) >> 3) & 0b0001_1111u8, 74 ((*self.slice.get_unchecked(2) << 5) & 0b1110_0000u8) 75 | (*self.slice.get_unchecked(3) & 0b0001_1111u8), 76 ])) 77 } 78 } 79 80 /// True if more fragment packets will follow. False if this is the last packet. 81 #[inline] more_fragments(&self) -> bool82 pub fn more_fragments(&self) -> bool { 83 // SAFETY: 84 // Slice size checked to be at least 8 bytes in constructor. 85 unsafe { 0 != *self.slice.get_unchecked(3) & 0b1000_0000u8 } 86 } 87 88 /// Identifcation value generated by the source identification(&self) -> u3289 pub fn identification(&self) -> u32 { 90 // SAFETY: 91 // Slice size checked to be at least 8 bytes in constructor. 92 unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) } 93 } 94 95 /// Checks if the fragment header actually fragments the packet. 96 /// 97 /// Returns false if the fragment offset is 0 and the more flag 98 /// is not set. Otherwise returns true. 99 /// 100 /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly 101 /// states that fragment headers that don't fragment the packet payload are 102 /// allowed. See the following quote from 103 /// RFC8200 page 32: 104 /// 105 /// > Revised the text to handle the case of fragments that are whole 106 /// > datagrams (i.e., both the Fragment Offset field and the M flag 107 /// > are zero). If received, they should be processed as a 108 /// > reassembled packet. Any other fragments that match should be 109 /// > processed independently. The Fragment creation process was 110 /// > modified to not create whole datagram fragments (Fragment 111 /// > Offset field and the M flag are zero). See 112 /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and 113 /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more 114 /// > information." 115 /// 116 /// ``` 117 /// use etherparse::Ipv6FragmentHeaderSlice; 118 /// 119 /// { 120 /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[ 121 /// 0, 0, 0, 0, // offset 0 & more_fragments not set 122 /// 1, 2, 3, 4, 123 /// ]).unwrap(); 124 /// assert!(false == slice.is_fragmenting_payload()); 125 /// } 126 /// 127 /// { 128 /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[ 129 /// 0, 0, 0, 0b1000_0000u8, // more_fragments set 130 /// 1, 2, 3, 4, 131 /// ]).unwrap(); 132 /// assert!(slice.is_fragmenting_payload()); 133 /// } 134 /// 135 /// { 136 /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[ 137 /// 0, 0, 1, 0, // non zero offset 138 /// 1, 2, 3, 4, 139 /// ]).unwrap(); 140 /// assert!(slice.is_fragmenting_payload()); 141 /// } 142 /// ``` 143 #[inline] is_fragmenting_payload(&self) -> bool144 pub fn is_fragmenting_payload(&self) -> bool { 145 // SAFETY: 146 // Slice size checked to be at least 8 bytes in constructor. 147 unsafe { 148 0 != *self.slice.get_unchecked(2) || 0 != (*self.slice.get_unchecked(3) & 0b1001_1111u8) 149 // exclude the reserved bytes 150 } 151 } 152 153 /// Decode some of the fields and copy the results to a 154 /// Ipv6FragmentHeader struct. to_header(&self) -> Ipv6FragmentHeader155 pub fn to_header(&self) -> Ipv6FragmentHeader { 156 Ipv6FragmentHeader { 157 next_header: self.next_header(), 158 fragment_offset: self.fragment_offset(), 159 more_fragments: self.more_fragments(), 160 identification: self.identification(), 161 } 162 } 163 } 164 165 #[cfg(test)] 166 mod test { 167 use crate::{test_gens::*, *}; 168 use alloc::{format, vec::Vec}; 169 use proptest::prelude::*; 170 171 proptest! { 172 #[test] 173 fn debug(input in ipv6_fragment_any()) { 174 let bytes = input.to_bytes(); 175 let slice = Ipv6FragmentHeaderSlice::from_slice( 176 &bytes 177 ).unwrap(); 178 assert_eq!( 179 &format!( 180 "Ipv6FragmentHeaderSlice {{ slice: {:?} }}", 181 slice.slice() 182 ), 183 &format!("{:?}", slice) 184 ); 185 } 186 } 187 188 proptest! { 189 #[test] 190 fn clone_eq(input in ipv6_fragment_any()) { 191 let bytes = input.to_bytes(); 192 let slice = Ipv6FragmentHeaderSlice::from_slice( 193 &bytes 194 ).unwrap(); 195 assert_eq!(slice, slice.clone()); 196 } 197 } 198 199 proptest! { 200 #[test] 201 fn from_slice( 202 input in ipv6_fragment_any(), 203 dummy_data in proptest::collection::vec(any::<u8>(), 0..20) 204 ) { 205 // serialize 206 let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len()); 207 input.write(&mut buffer).unwrap(); 208 buffer.extend(&dummy_data[..]); 209 210 // calls with a valid result 211 { 212 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap(); 213 assert_eq!(slice.slice(), &buffer[..8]); 214 } 215 216 // call with not enough data in the slice 217 for len in 0..Ipv6FragmentHeader::LEN { 218 assert_eq!( 219 Ipv6FragmentHeaderSlice::from_slice(&buffer[0..len]).unwrap_err(), 220 err::LenError{ 221 required_len: 8, 222 len: len, 223 len_source: LenSource::Slice, 224 layer: err::Layer::Ipv6FragHeader, 225 layer_start_offset: 0, 226 } 227 ); 228 } 229 } 230 } 231 232 proptest! { 233 #[test] 234 fn from_slice_unchecked( 235 input in ipv6_fragment_any(), 236 dummy_data in proptest::collection::vec(any::<u8>(), 0..20) 237 ) { 238 // serialize 239 let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len()); 240 input.write(&mut buffer).unwrap(); 241 buffer.extend(&dummy_data[..]); 242 243 // calls with a valid result 244 unsafe { 245 let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(&buffer[..]); 246 assert_eq!(slice.slice(), &buffer[..8]); 247 } 248 } 249 } 250 251 proptest! { 252 #[test] 253 fn getters(input in ipv6_fragment_any()) { 254 let buffer = input.to_bytes(); 255 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap(); 256 257 assert_eq!(input.next_header, slice.next_header()); 258 assert_eq!(input.fragment_offset, slice.fragment_offset()); 259 assert_eq!(input.more_fragments, slice.more_fragments()); 260 assert_eq!(input.identification, slice.identification()); 261 } 262 } 263 264 proptest! { 265 #[test] 266 fn is_fragmenting_payload( 267 non_zero_offset in 1u16..0b0001_1111_1111_1111u16, 268 identification in any::<u32>(), 269 next_header in ip_number_any(), 270 ) { 271 // negative case 272 { 273 let header = Ipv6FragmentHeader { 274 next_header, 275 fragment_offset: 0.try_into().unwrap(), 276 more_fragments: false, 277 identification 278 }; 279 // slice 280 let buffer = header.to_bytes(); 281 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); 282 assert!(false == slice.is_fragmenting_payload()); 283 } 284 // positive case (non zero offset) 285 { 286 let header = Ipv6FragmentHeader { 287 next_header, 288 fragment_offset: non_zero_offset.try_into().unwrap(), 289 more_fragments: false, 290 identification 291 }; 292 // slice 293 let buffer = header.to_bytes(); 294 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); 295 assert!(slice.is_fragmenting_payload()); 296 } 297 298 // positive case (more fragments) 299 { 300 let header = Ipv6FragmentHeader { 301 next_header, 302 fragment_offset: 0.try_into().unwrap(), 303 more_fragments: true, 304 identification 305 }; 306 // slice 307 let buffer = header.to_bytes(); 308 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); 309 assert!(slice.is_fragmenting_payload()); 310 } 311 312 // positive case (non zero offset & more fragments) 313 { 314 let header = Ipv6FragmentHeader { 315 next_header, 316 fragment_offset: non_zero_offset.try_into().unwrap(), 317 more_fragments: true, 318 identification 319 }; 320 // slice 321 let buffer = header.to_bytes(); 322 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); 323 assert!(slice.is_fragmenting_payload()); 324 } 325 } 326 } 327 328 proptest! { 329 #[test] 330 fn to_header(input in ipv6_fragment_any()) { 331 let buffer = input.to_bytes(); 332 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); 333 assert_eq!(input, slice.to_header()); 334 } 335 } 336 } 337