1 use crate::{ 2 err::{ip, Layer, LenError}, 3 *, 4 }; 5 6 /// Slice containing the IP header (v4 or v6), extension headers & 7 /// payload. 8 #[derive(Clone, Debug, Eq, PartialEq)] 9 pub enum IpSlice<'a> { 10 /// The ipv4 header & the decoded extension headers. 11 Ipv4(Ipv4Slice<'a>), 12 /// The ipv6 header & the decoded extension headers. 13 Ipv6(Ipv6Slice<'a>), 14 } 15 16 impl<'a> IpSlice<'a> { 17 /// Returns a reference to the `Ipv4Slice` if `self` is a `IpSlice::Ipv4`. ipv4(&self) -> Option<&Ipv4Slice>18 pub fn ipv4(&self) -> Option<&Ipv4Slice> { 19 use IpSlice::*; 20 match self { 21 Ipv4(slice) => Some(slice), 22 Ipv6(_) => None, 23 } 24 } 25 26 /// Returns a reference to the `Ipv6Slice` if `self` is a `IpSlice::Ipv6`. ipv6(&self) -> Option<&Ipv6Slice>27 pub fn ipv6(&self) -> Option<&Ipv6Slice> { 28 use IpSlice::*; 29 match self { 30 Ipv4(_) => None, 31 Ipv6(slice) => Some(slice), 32 } 33 } 34 35 /// Returns true if the payload is fragmented. is_fragmenting_payload(&self) -> bool36 pub fn is_fragmenting_payload(&self) -> bool { 37 match self { 38 IpSlice::Ipv4(s) => s.is_payload_fragmented(), 39 IpSlice::Ipv6(s) => s.is_payload_fragmented(), 40 } 41 } 42 43 /// Return the source address as an std::net::Ipvddr (requires 44 /// crate feature `std`). 45 #[cfg(feature = "std")] 46 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] source_addr(&self) -> std::net::IpAddr47 pub fn source_addr(&self) -> std::net::IpAddr { 48 match self { 49 IpSlice::Ipv4(s) => s.header().source_addr().into(), 50 IpSlice::Ipv6(s) => s.header().source_addr().into(), 51 } 52 } 53 54 /// Return the destination address as an std::net::IpAddr (requires 55 /// crate feature `std`). 56 #[cfg(feature = "std")] 57 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] destination_addr(&self) -> std::net::IpAddr58 pub fn destination_addr(&self) -> std::net::IpAddr { 59 match self { 60 IpSlice::Ipv4(s) => s.header().destination_addr().into(), 61 IpSlice::Ipv6(s) => s.header().destination_addr().into(), 62 } 63 } 64 65 /// Returns a slice containing the data after the IP header 66 /// and IP extensions headers. 67 #[inline] payload(&self) -> &IpPayloadSlice<'a>68 pub fn payload(&self) -> &IpPayloadSlice<'a> { 69 use IpSlice::*; 70 match self { 71 Ipv4(ipv4) => ipv4.payload(), 72 Ipv6(ipv6) => ipv6.payload(), 73 } 74 } 75 76 /// Returns the ip number the type of payload of the IP packet. 77 /// 78 /// This function returns the ip number stored in the last 79 /// IP header or extension header. 80 #[inline] payload_ip_number(&self) -> IpNumber81 pub fn payload_ip_number(&self) -> IpNumber { 82 use IpSlice::*; 83 match self { 84 Ipv4(ipv4) => ipv4.payload().ip_number, 85 Ipv6(ipv6) => ipv6.payload().ip_number, 86 } 87 } 88 89 /// Separates and validates IP headers (including extension headers) 90 /// in the given slice and determine the sub-slice containing the payload 91 /// of the IP packet. from_slice(slice: &[u8]) -> Result<IpSlice, err::ip::SliceError>92 pub fn from_slice(slice: &[u8]) -> Result<IpSlice, err::ip::SliceError> { 93 use crate::ip_number::AUTH; 94 use err::ip::{HeaderError::*, HeadersError::*, SliceError::*}; 95 use IpSlice::*; 96 97 if slice.is_empty() { 98 Err(Len(err::LenError { 99 required_len: 1, 100 len: slice.len(), 101 len_source: LenSource::Slice, 102 layer: err::Layer::IpHeader, 103 layer_start_offset: 0, 104 })) 105 } else { 106 // SAFETY: Safe as slice is not empty. 107 let first_byte = unsafe { slice.get_unchecked(0) }; 108 match first_byte >> 4 { 109 4 => { 110 let ihl = first_byte & 0xf; 111 112 // check that the ihl has at least the length of the base IPv4 header 113 if ihl < 5 { 114 return Err(IpHeaders(Ip(Ipv4HeaderLengthSmallerThanHeader { ihl }))); 115 } 116 117 // check there is enough data for the header 118 let header_len = (usize::from(ihl)) * 4; 119 if slice.len() < header_len { 120 return Err(Len(err::LenError { 121 required_len: header_len, 122 len: slice.len(), 123 len_source: LenSource::Slice, 124 layer: err::Layer::Ipv4Header, 125 layer_start_offset: 0, 126 })); 127 } 128 129 // SAFETY: 130 // Safe as the slice length is checked to be at least 131 // header_len or greater above. 132 let header = unsafe { 133 Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts( 134 slice.as_ptr(), 135 header_len, 136 )) 137 }; 138 139 // check the total_length at least contains the header 140 let total_len = usize::from(header.total_len()); 141 if total_len < header_len { 142 return Err(Len(LenError { 143 required_len: header_len, 144 len: total_len, 145 len_source: LenSource::Ipv4HeaderTotalLen, 146 layer: Layer::Ipv4Packet, 147 layer_start_offset: 0, 148 })); 149 } 150 151 // validate the total length against the slice 152 let header_payload = if slice.len() < total_len { 153 return Err(Len(LenError { 154 required_len: total_len, 155 len: slice.len(), 156 len_source: LenSource::Slice, 157 layer: Layer::Ipv4Packet, 158 layer_start_offset: 0, 159 })); 160 } else { 161 unsafe { 162 core::slice::from_raw_parts( 163 // SAFETY: Safe as slice.len() >= header_len was validated 164 // in a if statement above. 165 slice.as_ptr().add(header_len), 166 // SAFETY: Safe as total_length >= header_len was verified in an 167 // if statement above as well as that slice.len() >= total_length_usize. 168 total_len - header_len, 169 ) 170 } 171 }; 172 173 // slice extension headers 174 // decode the authentication header if needed 175 let fragmented = header.is_fragmenting_payload(); 176 match header.protocol() { 177 AUTH => { 178 use crate::err::ip_auth::HeaderSliceError as E; 179 180 // parse extension headers 181 let auth = match IpAuthHeaderSlice::from_slice(header_payload) { 182 Ok(s) => s, 183 Err(err) => match err { 184 E::Len(mut l) => { 185 // change the length source to the ipv4 header 186 l.len_source = LenSource::Ipv4HeaderTotalLen; 187 l.layer_start_offset += header.slice().len(); 188 return Err(Len(l)); 189 } 190 E::Content(err) => { 191 return Err(IpHeaders(ip::HeadersError::Ipv4Ext(err))) 192 } 193 }, 194 }; 195 196 // remove the extension header from the payload 197 let payload = unsafe { 198 core::slice::from_raw_parts( 199 header_payload.as_ptr().add(auth.slice().len()), 200 header_payload.len() - auth.slice().len(), 201 ) 202 }; 203 Ok(Ipv4(Ipv4Slice { 204 header, 205 exts: Ipv4ExtensionsSlice { auth: Some(auth) }, 206 payload: IpPayloadSlice { 207 ip_number: auth.next_header(), 208 fragmented, 209 len_source: LenSource::Ipv4HeaderTotalLen, 210 payload, 211 }, 212 })) 213 } 214 ip_number => Ok(Ipv4(Ipv4Slice { 215 header, 216 exts: Ipv4ExtensionsSlice { auth: None }, 217 payload: IpPayloadSlice { 218 ip_number, 219 fragmented, 220 len_source: LenSource::Ipv4HeaderTotalLen, 221 payload: header_payload, 222 }, 223 })), 224 } 225 } 226 6 => { 227 // check length 228 if slice.len() < Ipv6Header::LEN { 229 return Err(Len(err::LenError { 230 required_len: Ipv6Header::LEN, 231 len: slice.len(), 232 len_source: LenSource::Slice, 233 layer: err::Layer::Ipv6Header, 234 layer_start_offset: 0, 235 })); 236 } 237 238 let header = unsafe { 239 Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts( 240 slice.as_ptr(), 241 Ipv6Header::LEN, 242 )) 243 }; 244 245 // restrict slice by the length specified in the header 246 let (header_payload, len_source) = 247 if 0 == header.payload_length() && slice.len() > Ipv6Header::LEN { 248 // In case the payload_length is 0 assume that the entire 249 // rest of the slice is part of the packet until the jumbogram 250 // parameters can be parsed. 251 252 // TODO: Add payload length parsing from the jumbogram 253 ( 254 unsafe { 255 core::slice::from_raw_parts( 256 slice.as_ptr().add(Ipv6Header::LEN), 257 slice.len() - Ipv6Header::LEN, 258 ) 259 }, 260 LenSource::Slice, 261 ) 262 } else { 263 let payload_len = usize::from(header.payload_length()); 264 let expected_len = Ipv6Header::LEN + payload_len; 265 if slice.len() < expected_len { 266 return Err(Len(LenError { 267 required_len: expected_len, 268 len: slice.len(), 269 len_source: LenSource::Slice, 270 layer: Layer::Ipv6Packet, 271 layer_start_offset: 0, 272 })); 273 } else { 274 ( 275 unsafe { 276 core::slice::from_raw_parts( 277 slice.as_ptr().add(Ipv6Header::LEN), 278 payload_len, 279 ) 280 }, 281 LenSource::Ipv6HeaderPayloadLen, 282 ) 283 } 284 }; 285 286 // parse extension headers 287 let (exts, payload_ip_number, payload) = 288 Ipv6ExtensionsSlice::from_slice(header.next_header(), header_payload) 289 .map_err(|err| { 290 // modify length errors 291 use crate::err::ipv6_exts::HeaderSliceError as I; 292 match err { 293 I::Len(mut err) => { 294 err.len_source = LenSource::Ipv6HeaderPayloadLen; 295 err.layer_start_offset += Ipv6Header::LEN; 296 Len(err) 297 } 298 I::Content(err) => IpHeaders(ip::HeadersError::Ipv6Ext(err)), 299 } 300 })?; 301 302 let fragmented = exts.is_fragmenting_payload(); 303 Ok(Ipv6(Ipv6Slice { 304 header, 305 exts, 306 payload: IpPayloadSlice { 307 ip_number: payload_ip_number, 308 fragmented, 309 len_source, 310 payload, 311 }, 312 })) 313 } 314 version_number => Err(IpHeaders(Ip(UnsupportedIpVersion { version_number }))), 315 } 316 } 317 } 318 } 319 320 impl<'a> From<Ipv4Slice<'a>> for IpSlice<'a> { from(value: Ipv4Slice<'a>) -> Self321 fn from(value: Ipv4Slice<'a>) -> Self { 322 IpSlice::Ipv4(value) 323 } 324 } 325 326 impl<'a> From<Ipv6Slice<'a>> for IpSlice<'a> { from(value: Ipv6Slice<'a>) -> Self327 fn from(value: Ipv6Slice<'a>) -> Self { 328 IpSlice::Ipv6(value) 329 } 330 } 331 332 #[cfg(test)] 333 mod test { 334 use super::*; 335 use crate::test_gens::*; 336 use alloc::{format, vec::Vec}; 337 use proptest::prelude::*; 338 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; 339 340 #[test] debug_clone_eq()341 fn debug_clone_eq() { 342 // ipv4 343 { 344 let mut header: Ipv4Header = Default::default(); 345 header.protocol = ip_number::UDP; 346 header.set_payload_len(0).unwrap(); 347 let buffer = header.to_bytes(); 348 349 let ipv4 = Ipv4Slice::from_slice(&buffer).unwrap(); 350 let slice = IpSlice::Ipv4(ipv4.clone()); 351 352 // clone & eq 353 assert_eq!(slice.clone(), slice); 354 355 // debug 356 assert_eq!(format!("{:?}", slice), format!("Ipv4({:?})", ipv4)); 357 } 358 // ipv6 359 { 360 let header = Ipv6Header { 361 payload_length: 0, 362 next_header: ip_number::UDP, 363 ..Default::default() 364 }; 365 let buffer = header.to_bytes(); 366 let ipv6 = Ipv6Slice::from_slice(&buffer).unwrap(); 367 let slice = IpSlice::Ipv6(ipv6.clone()); 368 369 // clone & eq 370 assert_eq!(slice.clone(), slice); 371 372 // debug 373 assert_eq!(format!("{:?}", slice), format!("Ipv6({:?})", ipv6)); 374 } 375 } 376 377 #[test] is_fragmenting_payload()378 fn is_fragmenting_payload() { 379 for fragment in [false, true] { 380 use ip_number::UDP; 381 // ipv4 382 { 383 let mut ipv4 = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap(); 384 if fragment { 385 ipv4.fragment_offset = 123.try_into().unwrap(); 386 } 387 388 let data = ipv4.to_bytes(); 389 let ipv4_slice = Ipv4Slice::from_slice(&data).unwrap(); 390 assert_eq!(fragment, IpSlice::Ipv4(ipv4_slice).is_fragmenting_payload()); 391 } 392 393 // ipv6 394 { 395 let ipv6_frag = Ipv6FragmentHeader { 396 next_header: UDP, 397 fragment_offset: IpFragOffset::ZERO, 398 more_fragments: fragment, 399 identification: 0, 400 }; 401 let ipv6 = Ipv6Header { 402 traffic_class: 0, 403 flow_label: 1.try_into().unwrap(), 404 payload_length: ipv6_frag.header_len() as u16, 405 next_header: ip_number::IPV6_FRAG, 406 hop_limit: 4, 407 source: [1; 16], 408 destination: [2; 16], 409 }; 410 let mut data = Vec::with_capacity(ipv6.header_len() + ipv6_frag.header_len()); 411 data.extend_from_slice(&ipv6.to_bytes()); 412 data.extend_from_slice(&ipv6_frag.to_bytes()); 413 414 assert_eq!( 415 fragment, 416 IpSlice::Ipv6(Ipv6Slice::from_slice(&data).unwrap()).is_fragmenting_payload() 417 ); 418 } 419 } 420 } 421 422 #[cfg(feature = "std")] 423 #[test] source_addr()424 fn source_addr() { 425 // ipv4 426 { 427 let data = Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]) 428 .unwrap() 429 .to_bytes(); 430 assert_eq!( 431 IpAddr::V4(Ipv4Addr::from([3, 4, 5, 6])), 432 IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).source_addr() 433 ); 434 } 435 436 // ipv6 437 { 438 let data = Ipv6Header { 439 traffic_class: 0, 440 flow_label: 1.try_into().unwrap(), 441 payload_length: 0, 442 next_header: ip_number::IGMP, 443 hop_limit: 4, 444 source: [1; 16], 445 destination: [2; 16], 446 } 447 .to_bytes(); 448 449 assert_eq!( 450 IpAddr::V6(Ipv6Addr::from([1; 16])), 451 IpSlice::Ipv6(Ipv6Slice::from_slice(&data[..]).unwrap()).source_addr() 452 ); 453 } 454 } 455 456 #[cfg(feature = "std")] 457 #[test] destination_addr()458 fn destination_addr() { 459 use crate::ip_number::UDP; 460 461 // ipv4 462 { 463 let data = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]) 464 .unwrap() 465 .to_bytes(); 466 467 assert_eq!( 468 IpAddr::V4(Ipv4Addr::from([7, 8, 9, 10])), 469 IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).destination_addr() 470 ); 471 } 472 473 // ipv6 474 { 475 let data = Ipv6Header { 476 traffic_class: 0, 477 flow_label: 1.try_into().unwrap(), 478 payload_length: 0, 479 next_header: ip_number::IGMP, 480 hop_limit: 4, 481 source: [1; 16], 482 destination: [2; 16], 483 } 484 .to_bytes(); 485 486 assert_eq!( 487 IpAddr::V6(Ipv6Addr::from([2; 16])), 488 IpSlice::Ipv6(Ipv6Slice::from_slice(&data).unwrap()).destination_addr() 489 ); 490 } 491 } 492 493 #[test] payload()494 fn payload() { 495 let payload: [u8; 4] = [1, 2, 3, 4]; 496 // ipv4 497 { 498 let header = Ipv4Header::new( 499 payload.len() as u16, 500 1, 501 ip_number::UDP, 502 [3, 4, 5, 6], 503 [7, 8, 9, 10], 504 ) 505 .unwrap(); 506 let mut data = Vec::with_capacity(header.header_len() + payload.len()); 507 data.extend_from_slice(&header.to_bytes()); 508 data.extend_from_slice(&payload); 509 assert_eq!( 510 IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).payload(), 511 &IpPayloadSlice { 512 ip_number: ip_number::UDP.into(), 513 fragmented: header.is_fragmenting_payload(), 514 len_source: LenSource::Ipv4HeaderTotalLen, 515 payload: &payload, 516 } 517 ); 518 } 519 520 // ipv6 521 { 522 let header = Ipv6Header { 523 traffic_class: 0, 524 flow_label: 1.try_into().unwrap(), 525 payload_length: payload.len() as u16, 526 next_header: ip_number::UDP, 527 hop_limit: 4, 528 source: [1; 16], 529 destination: [2; 16], 530 }; 531 let mut data = Vec::with_capacity(header.header_len() + payload.len()); 532 data.extend_from_slice(&header.to_bytes()); 533 data.extend_from_slice(&payload); 534 assert_eq!( 535 IpSlice::Ipv6(Ipv6Slice::from_slice(&data[..]).unwrap()).payload(), 536 &IpPayloadSlice { 537 ip_number: ip_number::UDP.into(), 538 fragmented: false, 539 len_source: LenSource::Ipv6HeaderPayloadLen, 540 payload: &payload, 541 } 542 ); 543 } 544 } 545 546 #[test] payload_ip_number()547 fn payload_ip_number() { 548 use crate::ip_number::{IGMP, UDP}; 549 550 // ipv4 551 { 552 let data = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]) 553 .unwrap() 554 .to_bytes(); 555 assert_eq!( 556 UDP, 557 IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).payload_ip_number() 558 ); 559 } 560 561 // ipv6 562 { 563 let data = Ipv6Header { 564 traffic_class: 0, 565 flow_label: 1.try_into().unwrap(), 566 payload_length: 0, 567 next_header: IGMP, 568 hop_limit: 4, 569 source: [1; 16], 570 destination: [2; 16], 571 } 572 .to_bytes(); 573 574 assert_eq!( 575 IGMP, 576 IpSlice::Ipv6(Ipv6Slice::from_slice(&data).unwrap()).payload_ip_number() 577 ); 578 } 579 } 580 581 proptest! { 582 #[test] 583 fn from_ip_slice( 584 ipv4_header in ipv4_any(), 585 ipv4_exts in ipv4_extensions_with(ip_number::UDP), 586 ipv6_header in ipv6_any(), 587 mut ipv6_exts in ipv6_extensions_with(ip_number::UDP) 588 ) { 589 let payload = [1,2,3,4]; 590 591 // setup header length & fields 592 let ipv4_header = { 593 let mut header = ipv4_header; 594 header.protocol = if ipv4_exts.auth.is_some() { 595 ip_number::AUTH 596 } else { 597 ip_number::UDP 598 }; 599 header.total_len = (header.header_len() + ipv4_exts.header_len() + payload.len()) as u16; 600 header.header_checksum = header.calc_header_checksum(); 601 header 602 }; 603 604 let ipv4 = IpHeaders::Ipv4( 605 ipv4_header.clone(), 606 ipv4_exts.clone() 607 ); 608 609 let ipv6_header = { 610 let mut header = ipv6_header; 611 header.next_header = ipv6_exts.set_next_headers(ip_number::UDP); 612 header.payload_length = (ipv6_exts.header_len() + payload.len()) as u16; 613 header 614 }; 615 616 let ipv6 = IpHeaders::Ipv6( 617 ipv6_header.clone(), 618 ipv6_exts.clone() 619 ); 620 621 // happy path v4 622 { 623 // build packet 624 let mut data = Vec::with_capacity(ipv4.header_len() + payload.len()); 625 ipv4.write(&mut data).unwrap(); 626 data.extend_from_slice(&payload); 627 628 // run test 629 let actual = IpSlice::from_slice(&data).unwrap(); 630 assert!(actual.ipv6().is_none()); 631 let actual = actual.ipv4().unwrap().clone(); 632 assert_eq!(actual.header.to_header(), ipv4_header); 633 assert_eq!(actual.extensions().to_header(), ipv4_exts); 634 assert_eq!( 635 actual.payload, 636 IpPayloadSlice{ 637 ip_number: ip_number::UDP.into(), 638 fragmented: ipv4_header.is_fragmenting_payload(), 639 len_source: LenSource::Ipv4HeaderTotalLen, 640 payload: &payload 641 } 642 ); 643 } 644 645 // happy path v6 646 { 647 // build packet 648 let mut data = Vec::with_capacity(ipv6.header_len() + payload.len()); 649 ipv6.write(&mut data).unwrap(); 650 data.extend_from_slice(&payload); 651 652 // run test 653 let actual = crate::IpSlice::from_slice(&data).unwrap(); 654 assert!(actual.ipv4().is_none()); 655 let actual = actual.ipv6().unwrap().clone(); 656 assert_eq!(actual.header.to_header(), ipv6_header); 657 assert_eq!( 658 Ipv6Extensions::from_slice( 659 ipv6_header.next_header, 660 actual.extensions().slice() 661 ).unwrap().0, 662 ipv6_exts 663 ); 664 assert_eq!( 665 actual.payload, 666 IpPayloadSlice{ 667 ip_number: ip_number::UDP.into(), 668 fragmented: ipv6_exts.is_fragmenting_payload(), 669 len_source: LenSource::Ipv6HeaderPayloadLen, 670 payload: &payload 671 } 672 ); 673 } 674 675 // ipv6 with zero payload length (should fallback to the slice length) 676 { 677 let ipv6_header = { 678 let mut header = ipv6_header.clone(); 679 // set the payload length to zero so the payload identifier 680 // has to fallback to the slice length 681 header.payload_length = 0; 682 header 683 }; 684 685 // build packet 686 let mut data = Vec::with_capacity(ipv6.header_len() + payload.len()); 687 ipv6_header.write(&mut data).unwrap(); 688 ipv6_exts.write(&mut data, ipv6_header.next_header).unwrap(); 689 data.extend_from_slice(&payload); 690 691 // run test 692 let actual = crate::IpSlice::from_slice(&data).unwrap(); 693 assert!(actual.ipv4().is_none()); 694 let actual = actual.ipv6().unwrap().clone(); 695 assert_eq!(actual.header.to_header(), ipv6_header); 696 assert_eq!( 697 Ipv6Extensions::from_slice( 698 ipv6_header.next_header, 699 actual.extensions().slice() 700 ).unwrap().0, 701 ipv6_exts 702 ); 703 assert_eq!( 704 actual.payload, 705 IpPayloadSlice{ 706 ip_number: ip_number::UDP.into(), 707 fragmented: ipv6_exts.is_fragmenting_payload(), 708 len_source: LenSource::Slice, 709 payload: &payload 710 } 711 ); 712 } 713 714 } 715 } 716 717 proptest! { 718 #[test] 719 fn from_ipv4_slice( 720 ipv4_header in ipv4_unknown() 721 ) { 722 let mut header = ipv4_header.clone(); 723 header.total_len = (header.header_len() + 4) as u16; 724 725 let mut buffer = Vec::with_capacity(header.total_len.into()); 726 buffer.extend_from_slice(&header.to_bytes()[..]); 727 buffer.extend_from_slice(&[1,2,3,4]); 728 let s = Ipv4Slice::from_slice(&buffer).unwrap(); 729 let actual: IpSlice = s.clone().into(); 730 assert_eq!(IpSlice::Ipv4(s), actual); 731 } 732 } 733 734 proptest! { 735 #[test] 736 fn from_ipv6_slice( 737 ipv6_header in ipv6_unknown() 738 ) { 739 let mut header = ipv6_header.clone(); 740 header.payload_length = 4; 741 742 let mut buffer = Vec::with_capacity(header.header_len() + 4); 743 buffer.extend_from_slice(&header.to_bytes()[..]); 744 buffer.extend_from_slice(&[1,2,3,4]); 745 let s = Ipv6Slice::from_slice(&buffer).unwrap(); 746 let actual: IpSlice = s.clone().into(); 747 assert_eq!(IpSlice::Ipv6(s), actual); 748 } 749 } 750 } 751