1 use super::super::*; 2 3 /// IPv6 fragment header. 4 #[derive(Clone, Debug, Eq, PartialEq)] 5 pub struct Ipv6FragmentHeader { 6 /// IP protocol number specifying the next header or transport layer protocol. 7 /// 8 /// See [IpNumber] or [ip_number] for a definition of the known values. 9 pub next_header: IpNumber, 10 /// Offset of the current IP payload relative to the start of the fragmented 11 /// packet payload. 12 pub fragment_offset: IpFragOffset, 13 /// True if more fragment packets will follow. False if this is the last packet. 14 pub more_fragments: bool, 15 /// Identifcation value generated by the source. 16 pub identification: u32, 17 } 18 19 impl Ipv6FragmentHeader { 20 /// Length of the serialized header. 21 pub const LEN: usize = 8; 22 23 /// Create a new fragmentation header with the given parameters. 24 /// 25 /// Note that the `fragment_offset` can only support values between 0 and 0x1fff (inclusive). new( next_header: IpNumber, fragment_offset: IpFragOffset, more_fragments: bool, identification: u32, ) -> Ipv6FragmentHeader26 pub const fn new( 27 next_header: IpNumber, 28 fragment_offset: IpFragOffset, 29 more_fragments: bool, 30 identification: u32, 31 ) -> Ipv6FragmentHeader { 32 Ipv6FragmentHeader { 33 next_header, 34 fragment_offset, 35 more_fragments, 36 identification, 37 } 38 } 39 40 /// Read an Ipv6FragmentHeader from a slice and return the header & unused parts of the slice. from_slice(slice: &[u8]) -> Result<(Ipv6FragmentHeader, &[u8]), err::LenError>41 pub fn from_slice(slice: &[u8]) -> Result<(Ipv6FragmentHeader, &[u8]), err::LenError> { 42 let s = Ipv6FragmentHeaderSlice::from_slice(slice)?; 43 let rest = &slice[8..]; 44 let header = s.to_header(); 45 Ok((header, rest)) 46 } 47 48 /// Read an fragment header from the current reader position. 49 #[cfg(feature = "std")] 50 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] read<T: std::io::Read + std::io::Seek + Sized>( reader: &mut T, ) -> Result<Ipv6FragmentHeader, std::io::Error>51 pub fn read<T: std::io::Read + std::io::Seek + Sized>( 52 reader: &mut T, 53 ) -> Result<Ipv6FragmentHeader, std::io::Error> { 54 let buffer = { 55 let mut buffer: [u8; 8] = [0; 8]; 56 reader.read_exact(&mut buffer)?; 57 buffer 58 }; 59 60 Ok(Ipv6FragmentHeader { 61 next_header: IpNumber(buffer[0]), 62 fragment_offset: unsafe { 63 // SAFE as the resulting number is guaranteed to have at most 64 // 13 bits. 65 IpFragOffset::new_unchecked(u16::from_be_bytes([ 66 (buffer[2] >> 3) & 0b0001_1111u8, 67 ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8), 68 ])) 69 }, 70 more_fragments: 0 != buffer[3] & 0b1000_0000u8, 71 identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]), 72 }) 73 } 74 75 /// Read an fragment header from the current reader position. 76 #[cfg(feature = "std")] 77 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] read_limited<T: std::io::Read + std::io::Seek + Sized>( reader: &mut crate::io::LimitedReader<T>, ) -> Result<Ipv6FragmentHeader, crate::err::io::LimitedReadError>78 pub fn read_limited<T: std::io::Read + std::io::Seek + Sized>( 79 reader: &mut crate::io::LimitedReader<T>, 80 ) -> Result<Ipv6FragmentHeader, crate::err::io::LimitedReadError> { 81 use err::Layer; 82 83 // set layer so errors contain the correct layer & offset 84 reader.start_layer(Layer::Ipv6FragHeader); 85 86 let buffer = { 87 let mut buffer: [u8; 8] = [0; 8]; 88 reader.read_exact(&mut buffer)?; 89 buffer 90 }; 91 92 Ok(Ipv6FragmentHeader { 93 next_header: IpNumber(buffer[0]), 94 fragment_offset: unsafe { 95 // SAFE as the resulting number is guaranteed to have at most 96 // 13 bits. 97 IpFragOffset::new_unchecked(u16::from_be_bytes([ 98 (buffer[2] >> 3) & 0b0001_1111u8, 99 ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8), 100 ])) 101 }, 102 more_fragments: 0 != buffer[3] & 0b1000_0000u8, 103 identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]), 104 }) 105 } 106 107 /// Writes a given IPv6 fragment header to the current position. 108 #[cfg(feature = "std")] 109 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error>110 pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> { 111 writer.write_all(&self.to_bytes()) 112 } 113 114 /// Length of the header in bytes. 115 #[inline] header_len(&self) -> usize116 pub fn header_len(&self) -> usize { 117 Ipv6FragmentHeader::LEN 118 } 119 120 /// Checks if the fragment header actually fragments the packet. 121 /// 122 /// Returns false if the fragment offset is 0 and the more flag 123 /// is not set. Otherwise returns true. 124 /// 125 /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly 126 /// states that fragment headers that don't fragment the packet payload are 127 /// allowed. See the following quote from 128 /// RFC8200 page 32: 129 /// 130 /// > Revised the text to handle the case of fragments that are whole 131 /// > datagrams (i.e., both the Fragment Offset field and the M flag 132 /// > are zero). If received, they should be processed as a 133 /// > reassembled packet. Any other fragments that match should be 134 /// > processed independently. The Fragment creation process was 135 /// > modified to not create whole datagram fragments (Fragment 136 /// > Offset field and the M flag are zero). See 137 /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and 138 /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more 139 /// > information." 140 /// 141 /// ``` 142 /// use etherparse::{Ipv6FragmentHeader, ip_number::UDP}; 143 /// 144 /// // offset 0 & no more fragments result in an unfragmented payload 145 /// { 146 /// let header = Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), false, 123); 147 /// assert!(false == header.is_fragmenting_payload()); 148 /// } 149 /// 150 /// // offset 0 & but more fragments will come -> fragmented 151 /// { 152 /// let header = Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), true, 123); 153 /// assert!(header.is_fragmenting_payload()); 154 /// } 155 /// 156 /// // offset non zero & no more fragments will come -> fragmented 157 /// { 158 /// let header = Ipv6FragmentHeader::new(UDP, 1.try_into().unwrap(), false, 123); 159 /// assert!(header.is_fragmenting_payload()); 160 /// } 161 /// ``` 162 #[inline] is_fragmenting_payload(&self) -> bool163 pub fn is_fragmenting_payload(&self) -> bool { 164 self.more_fragments || (0 != self.fragment_offset.value()) 165 } 166 167 /// Returns the serialized form of the header as a statically 168 /// sized byte array. 169 #[inline] to_bytes(&self) -> [u8; 8]170 pub fn to_bytes(&self) -> [u8; 8] { 171 let fo_be: [u8; 2] = self.fragment_offset.value().to_be_bytes(); 172 let id_be = self.identification.to_be_bytes(); 173 [ 174 self.next_header.0, 175 0, 176 (((fo_be[0] << 3) & 0b1111_1000u8) | ((fo_be[1] >> 5) & 0b0000_0111u8)), 177 ((fo_be[1] & 0b0001_1111u8) 178 | if self.more_fragments { 179 0b1000_0000u8 180 } else { 181 0 182 }), 183 id_be[0], 184 id_be[1], 185 id_be[2], 186 id_be[3], 187 ] 188 } 189 } 190 191 #[cfg(test)] 192 mod test { 193 use crate::{test_gens::*, *}; 194 use alloc::{format, vec::Vec}; 195 use proptest::prelude::*; 196 use std::io::Cursor; 197 198 proptest! { 199 #[test] 200 fn debug(input in ipv6_fragment_any()) { 201 assert_eq!( 202 &format!( 203 "Ipv6FragmentHeader {{ next_header: {:?}, fragment_offset: {:?}, more_fragments: {}, identification: {} }}", 204 input.next_header, 205 input.fragment_offset, 206 input.more_fragments, 207 input.identification 208 ), 209 &format!("{:?}", input) 210 ); 211 } 212 } 213 214 proptest! { 215 #[test] 216 fn clone_eq(input in ipv6_fragment_any()) { 217 assert_eq!(input, input.clone()); 218 } 219 } 220 221 proptest! { 222 #[test] 223 fn new( 224 next_header in ip_number_any(), 225 fragment_offset in 0..IpFragOffset::MAX_U16, 226 more_fragments in any::<bool>(), 227 identification in any::<u32>(), 228 ) { 229 let a = Ipv6FragmentHeader::new( 230 next_header, 231 fragment_offset.try_into().unwrap(), 232 more_fragments, 233 identification 234 ); 235 assert_eq!(next_header, a.next_header); 236 assert_eq!(fragment_offset, a.fragment_offset.value()); 237 assert_eq!(more_fragments, a.more_fragments); 238 assert_eq!(identification, a.identification); 239 } 240 } 241 242 proptest! { 243 #[test] 244 fn from_slice( 245 input in ipv6_fragment_any(), 246 dummy_data in proptest::collection::vec(any::<u8>(), 0..20) 247 ) { 248 // serialize 249 let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len()); 250 input.write(&mut buffer).unwrap(); 251 buffer.extend(&dummy_data[..]); 252 253 // calls with a valid result 254 { 255 let (result, rest) = Ipv6FragmentHeader::from_slice(&buffer[..]).unwrap(); 256 assert_eq!(input, result); 257 assert_eq!(&buffer[8..], rest); 258 } 259 // call with not enough data in the slice 260 for len in 0..Ipv6FragmentHeader::LEN { 261 assert_eq!( 262 Ipv6FragmentHeader::from_slice(&buffer[0..len]).unwrap_err(), 263 err::LenError{ 264 required_len: Ipv6FragmentHeader::LEN, 265 len: len, 266 len_source: LenSource::Slice, 267 layer: err::Layer::Ipv6FragHeader, 268 layer_start_offset: 0, 269 } 270 ); 271 } 272 } 273 } 274 275 proptest! { 276 #[test] 277 fn read( 278 input in ipv6_fragment_any(), 279 dummy_data in proptest::collection::vec(any::<u8>(), 0..20) 280 ) { 281 use std::io::ErrorKind; 282 283 // serialize 284 let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len()); 285 input.write(&mut buffer).unwrap(); 286 buffer.extend(&dummy_data[..]); 287 288 // calls with a valid result 289 { 290 let mut cursor = Cursor::new(&buffer); 291 let result = Ipv6FragmentHeader::read(&mut cursor).unwrap(); 292 assert_eq!(input, result); 293 assert_eq!(cursor.position(), 8); 294 } 295 296 // call with not enough data in the slice 297 for len in 0..Ipv6FragmentHeader::LEN { 298 let mut cursor = Cursor::new(&buffer[0..len]); 299 assert_eq!( 300 Ipv6FragmentHeader::read(&mut cursor) 301 .unwrap_err() 302 .kind(), 303 ErrorKind::UnexpectedEof 304 ); 305 } 306 } 307 } 308 309 proptest! { 310 #[test] 311 fn write(input in ipv6_fragment_any()) { 312 313 // normal write 314 { 315 let mut buffer = Vec::with_capacity(8); 316 input.write(&mut buffer).unwrap(); 317 assert_eq!( 318 &buffer, 319 &input.to_bytes() 320 ); 321 } 322 323 // not enough memory for write 324 for len in 0..Ipv6FragmentHeader::LEN { 325 let mut buffer = [0u8;Ipv6FragmentHeader::LEN]; 326 let mut cursor = Cursor::new(&mut buffer[..len]); 327 assert!( 328 input.write(&mut cursor).is_err() 329 ); 330 } 331 } 332 } 333 334 proptest! { 335 #[test] 336 fn header_len(input in ipv6_fragment_any()) { 337 assert_eq!(8, input.header_len()); 338 } 339 } 340 341 proptest! { 342 #[test] 343 fn is_fragmenting_payload( 344 non_zero_offset in 1u16..0b0001_1111_1111_1111u16, 345 identification in any::<u32>(), 346 next_header in ip_number_any(), 347 348 ) { 349 // negative case 350 { 351 let header = Ipv6FragmentHeader { 352 next_header, 353 fragment_offset: 0.try_into().unwrap(), 354 more_fragments: false, 355 identification 356 }; 357 assert!(false == header.is_fragmenting_payload()); 358 } 359 // positive case (non zero offset) 360 { 361 let header = Ipv6FragmentHeader { 362 next_header, 363 fragment_offset: non_zero_offset.try_into().unwrap(), 364 more_fragments: false, 365 identification 366 }; 367 assert!(header.is_fragmenting_payload()); 368 } 369 370 // positive case (more fragments) 371 { 372 let header = Ipv6FragmentHeader { 373 next_header, 374 fragment_offset: 0.try_into().unwrap(), 375 more_fragments: true, 376 identification 377 }; 378 assert!(header.is_fragmenting_payload()); 379 } 380 381 // positive case (non zero offset & more fragments) 382 { 383 let header = Ipv6FragmentHeader { 384 next_header, 385 fragment_offset: non_zero_offset.try_into().unwrap(), 386 more_fragments: true, 387 identification 388 }; 389 assert!(header.is_fragmenting_payload()); 390 } 391 } 392 } 393 394 proptest! { 395 #[test] 396 fn to_bytes(input in ipv6_fragment_any()) { 397 398 // normal write 399 { 400 let fragment_offset_be = input.fragment_offset.value().to_be_bytes(); 401 let id_be = input.identification.to_be_bytes(); 402 assert_eq!( 403 &input.to_bytes(), 404 &[ 405 input.next_header.0, 406 0, 407 ( 408 (fragment_offset_be[0] << 3 & 0b1111_1000u8) | 409 (fragment_offset_be[1] >> 5 & 0b0000_0111u8) 410 ), 411 ( 412 (fragment_offset_be[1] & 0b0001_1111u8) | 413 if input.more_fragments { 414 0b1000_0000u8 415 } else { 416 0u8 417 } 418 ), 419 id_be[0], 420 id_be[1], 421 id_be[2], 422 id_be[3], 423 ] 424 ); 425 } 426 } 427 } 428 } 429