#[cfg(feature = "std")] use crate::err::ip::HeadersWriteError; use crate::err::{Layer, LenError, ValueTooBigError}; use crate::*; /// Internet protocol headers version 4 & 6. #[derive(Clone, Debug, Eq, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum IpHeaders { /// IPv4 header & extension headers. Ipv4(Ipv4Header, Ipv4Extensions), /// IPv6 header & extension headers. Ipv6(Ipv6Header, Ipv6Extensions), } impl IpHeaders { /// Maximum summed up length of all extension headers in bytes/octets. pub const MAX_LEN: usize = Ipv6Header::LEN + Ipv6Extensions::MAX_LEN; /// Returns references to the IPv4 header & extensions if the header contains IPv4 values. pub fn ipv4(&self) -> Option<(&Ipv4Header, &Ipv4Extensions)> { if let IpHeaders::Ipv4(header, exts) = self { Some((header, exts)) } else { None } } /// Returns references to the IPv6 header & extensions if the header contains IPv6 values. pub fn ipv6(&self) -> Option<(&Ipv6Header, &Ipv6Extensions)> { if let IpHeaders::Ipv6(header, exts) = self { Some((header, exts)) } else { None } } /// Renamed to [`IpHeaders::from_slice`] #[deprecated(since = "0.10.1", note = "Renamed to `IpHeaders::from_slice`")] #[inline] pub fn read_from_slice( slice: &[u8], ) -> Result<(IpHeaders, IpNumber, &[u8]), err::ip::HeadersSliceError> { let (header, payload) = IpHeaders::from_slice(slice)?; Ok((header, payload.ip_number, payload.payload)) } /// Read an [`IpHeaders`] from a slice and return the headers & payload of /// the IP packet (determined based on the length fields in the IP header). /// /// Note that his function returns an [`crate::err::LenError`] if the given slice /// contains less data then the length fields in the IP header indicate should /// be present. /// /// If you want to ignore these kind of length errors based on the length /// fields in the IP headers use [`IpHeaders::from_slice_lax`] instead. pub fn from_slice( slice: &[u8], ) -> Result<(IpHeaders, IpPayloadSlice<'_>), err::ip::HeadersSliceError> { use err::ip::{HeaderError::*, HeadersError::*, HeadersSliceError::*}; if slice.is_empty() { Err(Len(err::LenError { required_len: 1, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::IpHeader, layer_start_offset: 0, })) } else { match slice[0] >> 4 { 4 => { // check length if slice.len() < Ipv4Header::MIN_LEN { return Err(Len(err::LenError { required_len: Ipv4Header::MIN_LEN, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })); } // read ihl // // SAFETY: // Safe as the slice length is checked to be at least // Ipv4Header::MIN_LEN (20) at the start. let ihl = unsafe { slice.get_unchecked(0) } & 0xf; //check that the ihl is correct if ihl < 5 { return Err(Content(Ip(Ipv4HeaderLengthSmallerThanHeader { ihl }))); } // check that the slice contains enough data for the entire header + options let header_len = usize::from(ihl) * 4; if slice.len() < header_len { return Err(Len(LenError { required_len: header_len, len: slice.len(), len_source: LenSource::Slice, layer: Layer::Ipv4Header, layer_start_offset: 0, })); } let header = unsafe { // SAFETY: Safe as the IHL & slice len has been validated Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts( slice.as_ptr(), header_len, )) .to_header() }; // check that the total len is at least containing the header len let total_len: usize = header.total_len.into(); if total_len < header_len { return Err(Len(LenError { required_len: header_len, len: total_len, len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::Ipv4Packet, layer_start_offset: 0, })); } // restrict the rest of the slice based on the total len let rest = if slice.len() < total_len { return Err(Len(LenError { required_len: total_len, len: slice.len(), len_source: LenSource::Slice, layer: Layer::Ipv4Packet, layer_start_offset: 0, })); } else { unsafe { core::slice::from_raw_parts( // SAFETY: Safe as the slice length was validated to be at least header_length slice.as_ptr().add(header_len), // SAFETY: Safe as slice length has been validated to be at least total_length_usize long total_len - header_len, ) } }; let (exts, next_protocol, rest) = Ipv4Extensions::from_slice(header.protocol, rest).map_err(|err| { use err::ip_auth::HeaderSliceError as I; match err { I::Len(mut err) => { err.layer_start_offset += header_len; err.len_source = LenSource::Ipv4HeaderTotalLen; Len(err) } I::Content(err) => Content(Ipv4Ext(err)), } })?; let fragmented = header.is_fragmenting_payload(); Ok(( IpHeaders::Ipv4(header, exts), IpPayloadSlice { ip_number: next_protocol, fragmented, len_source: LenSource::Ipv4HeaderTotalLen, payload: rest, }, )) } 6 => { if slice.len() < Ipv6Header::LEN { return Err(Len(err::LenError { required_len: Ipv6Header::LEN, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, })); } let header = { // SAFETY: // This is safe as the slice length is checked to be // at least Ipv6Header::LEN (40) before this code block. unsafe { Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts( slice.as_ptr(), Ipv6Header::LEN, )) .to_header() } }; // restrict slice by the length specified in the header let (header_payload, len_source) = if 0 == header.payload_length && slice.len() > Ipv6Header::LEN { // In case the payload_length is 0 assume that the entire // rest of the slice is part of the packet until the jumbogram // parameters can be parsed. // TODO: Add payload length parsing from the jumbogram unsafe { ( core::slice::from_raw_parts( slice.as_ptr().add(Ipv6Header::LEN), slice.len() - Ipv6Header::LEN, ), LenSource::Slice, ) } } else { let payload_len: usize = header.payload_length.into(); let expected_len = Ipv6Header::LEN + payload_len; if slice.len() < expected_len { return Err(Len(LenError { required_len: expected_len, len: slice.len(), len_source: LenSource::Slice, layer: Layer::Ipv6Packet, layer_start_offset: 0, })); } else { unsafe { ( core::slice::from_raw_parts( slice.as_ptr().add(Ipv6Header::LEN), payload_len, ), LenSource::Ipv6HeaderPayloadLen, ) } } }; let (exts, next_header, rest) = Ipv6Extensions::from_slice(header.next_header, header_payload).map_err( |err| { use err::ipv6_exts::HeaderSliceError as I; match err { I::Len(mut err) => { err.layer_start_offset += Ipv6Header::LEN; err.len_source = len_source; Len(err) } I::Content(err) => Content(Ipv6Ext(err)), } }, )?; let fragmented = exts.is_fragmenting_payload(); Ok(( IpHeaders::Ipv6(header, exts), IpPayloadSlice { ip_number: next_header, fragmented, len_source, payload: rest, }, )) } version_number => Err(Content(Ip(UnsupportedIpVersion { version_number }))), } } } /// Reads an [`IpHeaders`] as far as possible without encountering an error & /// separates the payload from the given slice with less strict length checks. /// This function is usefull for cut off packet or for packets with unset length /// fields). /// /// If you want to only receive correct IpPayloads use [`IpHeaders::from_slice`] /// instead. /// /// The main usecases for this functions are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. /// * Parsing packets where the `total_len` (for IPv4) or `payload_length` (for IPv6) /// have not yet been set. This can be useful when parsing packets which have been /// recorded in a layer before the length field was set (e.g. before the operating /// system set the length fields). /// /// # Differences to `from_slice`: /// /// There are two main differences: /// /// * Errors in the expansion headers will only stop the parsing and return an `Ok` /// with the successfully parsed parts and the error as optional. Only if an /// unrecoverable error is encountered in the IP header itself an `Err` is returned. /// In the normal `from_slice` function an `Err` is returned if an error is /// encountered in an exteions header. /// * `from_slice_lax` ignores inconsistent `total_len` (in IPv4 headers) and /// inconsistent `payload_length` (in IPv6 headers) values. When these length /// values in the IP header are inconsistant the length of the given slice is /// used as a substitute. /// /// You can check if the slice length was used as a substitude by checking /// if the `len_source` value in the returned [`LaxIpPayloadSlice`] is set to /// [`LenSource::Slice`]. If a substitution was not needed `len_source` /// is set to [`LenSource::Ipv4HeaderTotalLen`] or /// [`LenSource::Ipv6HeaderPayloadLen`]. /// /// # When is the slice length used as a fallback? /// /// For IPv4 packets the slice length is used as a fallback/substitude /// if the `total_length` field in the IPv4 header is: /// /// * Bigger then the given slice (payload cannot fully be seperated). /// * Too small to contain at least the IPv4 header. /// /// For IPv6 packet the slice length is used as a fallback/substitude /// if the `payload_length` is /// /// * Bigger then the given slice (payload cannot fully be seperated). /// * The value `0`. pub fn from_slice_lax( slice: &[u8], ) -> Result< ( IpHeaders, LaxIpPayloadSlice<'_>, Option<(err::ip_exts::HeadersSliceError, Layer)>, ), err::ip::LaxHeaderSliceError, > { use err::ip::{HeaderError::*, LaxHeaderSliceError::*}; if slice.is_empty() { Err(Len(err::LenError { required_len: 1, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::IpHeader, layer_start_offset: 0, })) } else { match slice[0] >> 4 { 4 => { // check length if slice.len() < Ipv4Header::MIN_LEN { return Err(Len(err::LenError { required_len: Ipv4Header::MIN_LEN, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })); } // read ihl // // SAFETY: // Safe as the slice length is checked to be at least // Ipv4Header::MIN_LEN (20) at the start. let ihl = unsafe { slice.get_unchecked(0) } & 0xf; //check that the ihl is correct if ihl < 5 { return Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl })); } // check that the slice contains enough data for the entire header + options let header_len = usize::from(ihl) * 4; if slice.len() < header_len { return Err(Len(LenError { required_len: header_len, len: slice.len(), len_source: LenSource::Slice, layer: Layer::Ipv4Header, layer_start_offset: 0, })); } let header = unsafe { // SAFETY: Safe as the IHL & slice len has been validated Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts( slice.as_ptr(), header_len, )) .to_header() }; // check that the total len is at least containing the header len let total_len: usize = header.total_len.into(); // restrict the rest of the slice based on the total len (if the total_len is not conflicting) let (len_source, rest, incomplete) = if total_len < header_len { // fallback to slice len ( LenSource::Slice, unsafe { core::slice::from_raw_parts( // SAFETY: Safe as the slice length was validated to be at least header_length slice.as_ptr().add(header_len), // SAFETY: Safe as slice length has been validated to be at least header_len long slice.len() - header_len, ) }, false, ) } else if slice.len() < total_len { ( LenSource::Slice, unsafe { core::slice::from_raw_parts( // SAFETY: Safe as the slice length was validated to be at least header_length slice.as_ptr().add(header_len), // SAFETY: Safe as slice length has been validated to be at least header_len long slice.len() - header_len, ) }, true, ) } else { ( LenSource::Ipv4HeaderTotalLen, unsafe { core::slice::from_raw_parts( // SAFETY: Safe as the slice length was validated to be at least header_length slice.as_ptr().add(header_len), // SAFETY: Safe as slice length has been validated to be at least total_length_usize long total_len - header_len, ) }, false, ) }; let (exts, next_protocol, rest, stop_err) = Ipv4Extensions::from_slice_lax(header.protocol, rest); let stop_err = stop_err.map(|err| { use err::ip_auth::HeaderSliceError as I; use err::ip_exts::HeaderError as OC; use err::ip_exts::HeadersSliceError as O; match err { I::Len(mut l) => O::Len({ l.layer_start_offset += header_len; l.len_source = len_source; l }), I::Content(c) => O::Content(OC::Ipv4Ext(c)), } }); let fragmented = header.is_fragmenting_payload(); Ok(( IpHeaders::Ipv4(header, exts), LaxIpPayloadSlice { incomplete, ip_number: next_protocol, fragmented, len_source, payload: rest, }, stop_err.map(|v| (v, Layer::IpAuthHeader)), )) } 6 => { if slice.len() < Ipv6Header::LEN { return Err(Len(err::LenError { required_len: Ipv6Header::LEN, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, })); } let header = { // SAFETY: // This is safe as the slice length is checked to be // at least Ipv6Header::LEN (40) befpre this code block. unsafe { Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts( slice.as_ptr(), Ipv6Header::LEN, )) .to_header() } }; // restrict slice by the length specified in the header let payload_len = usize::from(header.payload_length); let (header_payload, len_source, incomplete) = if (header.payload_length == 0) && (Ipv6Header::LEN < slice.len()) { // TODO: Add payload length parsing from the jumbogram unsafe { ( core::slice::from_raw_parts( // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above. slice.as_ptr().add(Ipv6Header::LEN), // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above. slice.len() - Ipv6Header::LEN, ), LenSource::Slice, false, ) } } else if (slice.len() - Ipv6Header::LEN) < payload_len { unsafe { ( core::slice::from_raw_parts( // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above. slice.as_ptr().add(Ipv6Header::LEN), // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above. slice.len() - Ipv6Header::LEN, ), LenSource::Slice, true, ) } } else { unsafe { ( core::slice::from_raw_parts( // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above. slice.as_ptr().add(Ipv6Header::LEN), // SAFTEY: Safe as we verify that `(slice.len() - Ipv6Header::LEN) >= payload_len` above. payload_len, ), LenSource::Ipv6HeaderPayloadLen, false, ) } }; let (exts, next_header, rest, stop_err) = Ipv6Extensions::from_slice_lax(header.next_header, header_payload); let stop_err = stop_err.map(|(err, layer)| { use err::ip_exts::HeaderError::Ipv6Ext; use err::ip_exts::HeadersSliceError as O; use err::ipv6_exts::HeaderSliceError as I; ( match err { I::Len(mut l) => { l.layer_start_offset += Ipv6Header::LEN; l.len_source = len_source; O::Len(l) } I::Content(c) => O::Content(Ipv6Ext(c)), }, layer, ) }); let fragmented = exts.is_fragmenting_payload(); Ok(( IpHeaders::Ipv6(header, exts), LaxIpPayloadSlice { incomplete, ip_number: next_header, fragmented, len_source, payload: rest, }, stop_err, )) } version_number => Err(Content(UnsupportedIpVersion { version_number })), } } } /// Read an IPv4 header & extension headers from a slice and return the slice containing the payload /// according to the total_length field in the IPv4 header. /// /// Note that his function returns an [`err::LenError`] if the given slice /// contains less data then the `total_len` field in the IPv4 header indicates /// should be present. /// /// If you want to ignore these kind of length errors based on the length /// fields in the IP headers use [`IpHeaders::from_ipv4_slice_lax`] instead. pub fn from_ipv4_slice( slice: &[u8], ) -> Result<(IpHeaders, IpPayloadSlice<'_>), err::ipv4::SliceError> { use err::ipv4::SliceError::*; // read the header let (header, header_rest) = Ipv4Header::from_slice(slice).map_err(|err| { use err::ipv4::HeaderSliceError as I; match err { I::Len(err) => Len(err), I::Content(err) => Header(err), } })?; // check that the total length at least contains the header let total_len: usize = header.total_len.into(); let header_len = header.header_len(); let payload_len = if total_len >= header_len { total_len - header_len } else { return Err(Len(LenError { required_len: header_len, len: total_len, len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::Ipv4Packet, layer_start_offset: 0, })); }; // limit rest based on ipv4 total length let header_rest = if payload_len > header_rest.len() { return Err(Len(err::LenError { required_len: total_len, len: slice.len(), len_source: LenSource::Slice, layer: Layer::Ipv4Packet, layer_start_offset: 0, })); } else { unsafe { // Safe as the payload_len <= header_rest.len is verified above core::slice::from_raw_parts(header_rest.as_ptr(), payload_len) } }; // read the extension header let (exts, next_header, exts_rest) = Ipv4Extensions::from_slice(header.protocol, header_rest).map_err(|err| { use err::ip_auth::HeaderSliceError as I; match err { I::Len(mut err) => { err.layer_start_offset += header.header_len(); err.len_source = LenSource::Ipv4HeaderTotalLen; Len(err) } I::Content(err) => Exts(err), } })?; let fragmented = header.is_fragmenting_payload(); Ok(( IpHeaders::Ipv4(header, exts), IpPayloadSlice { ip_number: next_header, fragmented, len_source: LenSource::Ipv4HeaderTotalLen, payload: exts_rest, }, )) } /// Reads an IPv4 header & its extensions headers as far as is possible without encountering an /// error & separates the payload from the given slice with less strict length checks. /// This function is usefull for cut off packet or for packets with unset length fields). /// /// If you want to only receive correct IpPayloads use [`IpHeaders::from_ipv4_slice`] /// instead. /// /// The main usecases for this functions are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. /// * Parsing packets where the `total_len` (for IPv4) have not yet been set. /// This can be useful when parsing packets which have been recorded in a /// layer before the length field was set (e.g. before the operating /// system set the length fields). /// /// # Differences to `from_ipv4_slice`: /// /// There are two main differences: /// /// * Errors in the expansion headers will only stop the parsing and return an `Ok` /// with the successfully parsed parts and the error as optional. Only if an /// unrecoverable error is encountered in the IP header itself an `Err` is returned. /// In the normal `from_slice` function an `Err` is returned if an error is /// encountered in an exteions header. /// * `from_ipv4_slice_lax` ignores inconsistent `total_len` values. When the `total_len` /// value in the IPv4 header are inconsistant the length of the given slice is /// used as a substitute. /// /// You can check if the slice length was used as a substitude by checking /// if the `len_source` value in the returned [`crate::LaxIpPayloadSlice`] is set to /// [`LenSource::Slice`]. If a substitution was not needed `len_source` /// is set to [`LenSource::Ipv4HeaderTotalLen`]. /// /// # When is the slice length used as a fallback? /// /// For IPv4 packets the slice length is used as a fallback/substitude /// if the `total_length` field in the IPv4 header is: /// /// * Bigger then the given slice (payload cannot fully be seperated). /// * Too small to contain at least the IPv4 header. pub fn from_ipv4_slice_lax( slice: &[u8], ) -> Result< ( IpHeaders, LaxIpPayloadSlice<'_>, Option, ), err::ip::LaxHeaderSliceError, > { use err::ip::LaxHeaderSliceError::*; // read the header let (header, header_rest) = Ipv4Header::from_slice(slice).map_err(|err| { use err::ip::HeaderError as O; use err::ipv4::HeaderError as I1; use err::ipv4::HeaderSliceError as I0; match err { I0::Len(err) => Len(err), I0::Content(err) => Content(match err { I1::UnexpectedVersion { version_number } => { O::UnsupportedIpVersion { version_number } } I1::HeaderLengthSmallerThanHeader { ihl } => { O::Ipv4HeaderLengthSmallerThanHeader { ihl } } }), } })?; // check that the total len is at least containing the header len let total_len: usize = header.total_len.into(); // restrict the rest of the slice based on the total len (if the total_len is not conflicting) let header_len = header.header_len(); let (len_source, header_rest, incomplete) = if total_len < header_len { // fallback to the rest of the slice (LenSource::Slice, header_rest, false) } else if header_rest.len() < total_len - header_len { // fallback to the rest of the slice (LenSource::Slice, header_rest, true) } else { ( LenSource::Ipv4HeaderTotalLen, unsafe { core::slice::from_raw_parts( header_rest.as_ptr(), // SAFETY: Safe as slice length has been validated to be at least total_length_usize long total_len - header_len, ) }, false, ) }; let (exts, next_protocol, payload, mut stop_err) = Ipv4Extensions::from_slice_lax(header.protocol, header_rest); use err::ip_auth::HeaderSliceError as I; if let Some(I::Len(err)) = stop_err.as_mut() { err.layer_start_offset += header.header_len(); err.len_source = len_source; } let fragmented = header.is_fragmenting_payload(); Ok(( IpHeaders::Ipv4(header, exts), LaxIpPayloadSlice { incomplete, ip_number: next_protocol, fragmented, len_source, payload, }, stop_err, )) } /// Read an IPv6 header & extension headers from a slice and return the slice /// containing the payload (e.g. TCP, UDP etc.) length limited by payload_length /// field in the IPv6 header. /// /// Note that slice length is used as a fallback value in case the /// payload_length in the IPv6 is set to zero. This is a temporary workaround /// to partially support jumbograms. pub fn from_ipv6_slice( slice: &[u8], ) -> Result<(IpHeaders, IpPayloadSlice<'_>), err::ipv6::SliceError> { use err::ipv6::SliceError::*; // read ipv6 header let (header, header_rest) = Ipv6Header::from_slice(slice).map_err(|err| { use err::ipv6::HeaderSliceError as I; match err { I::Len(err) => Len(err), I::Content(err) => Header(err), } })?; // restrict slice by the length specified in the header let (header_payload, len_source) = if 0 == header.payload_length && slice.len() > Ipv6Header::LEN { // In case the payload_length is 0 assume that the entire // rest of the slice is part of the packet until the jumbogram // parameters can be parsed. // TODO: Add payload length parsing from the jumbogram (header_rest, LenSource::Slice) } else { let payload_len: usize = header.payload_length.into(); if header_rest.len() < payload_len { return Err(Len(LenError { required_len: payload_len + Ipv6Header::LEN, len: slice.len(), len_source: LenSource::Slice, layer: Layer::Ipv6Packet, layer_start_offset: 0, })); } else { unsafe { ( core::slice::from_raw_parts(header_rest.as_ptr(), payload_len), LenSource::Ipv6HeaderPayloadLen, ) } } }; // read ipv6 extensions headers let (exts, next_header, exts_rest) = Ipv6Extensions::from_slice(header.next_header, header_payload).map_err(|err| { use err::ipv6_exts::HeaderSliceError as I; match err { I::Len(mut err) => { err.layer_start_offset += Ipv6Header::LEN; err.len_source = len_source; Len(err) } I::Content(err) => Exts(err), } })?; let fragmented = exts.is_fragmenting_payload(); Ok(( IpHeaders::Ipv6(header, exts), IpPayloadSlice { ip_number: next_header, fragmented, len_source, payload: exts_rest, }, )) } /// Reads an IPv6 header & its extensions headers as far as is possible without encountering an /// error & separates the payload from the given slice with less strict length checks. /// This function is usefull for cut off packet or for packets with unset length fields). /// /// If you want to only receive correct IpPayloads use [`IpHeaders::from_ipv6_slice`] /// instead. /// /// The main usecases for this functions are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. /// * Parsing packets where the `payload_length` (in the IPv6 header) has not /// yet been set. This can be useful when parsing packets which have been /// recorded in a layer before the length field was set (e.g. before the operating /// system set the length fields). /// /// # Differences to `from_slice`: /// /// There are two main differences: /// /// * Errors in the expansion headers will only stop the parsing and return an `Ok` /// with the successfully parsed parts and the error as optional. Only if an /// unrecoverable error is encountered in the IP header itself an `Err` is returned. /// In the normal `from_slice` function an `Err` is returned if an error is /// encountered in an exteions header. /// * `from_slice_lax` ignores inconsistent `payload_length` values. When the /// `payload_length` value in the IPv6 header is inconsistant the length of /// the given slice is used as a substitute. /// /// You can check if the slice length was used as a substitude by checking /// if the `len_source` value in the returned [`LaxIpPayloadSlice`] is set to /// [`LenSource::Slice`]. If a substitution was not needed `len_source` /// is set to [`LenSource::Ipv6HeaderPayloadLen`]. /// /// # When is the slice length used as a fallback? /// /// The slice length is used as a fallback/substitude if the `payload_length` /// field in the IPv6 header is /// /// * Bigger then the given slice (payload cannot fully be seperated). /// * The value `0`. pub fn from_ipv6_slice_lax( slice: &[u8], ) -> Result< ( IpHeaders, LaxIpPayloadSlice<'_>, Option<(err::ipv6_exts::HeaderSliceError, Layer)>, ), err::ipv6::HeaderSliceError, > { // read ipv6 header let (header, header_rest) = Ipv6Header::from_slice(slice)?; // restrict slice by the length specified in the header let payload_len: usize = header.payload_length.into(); let (header_payload, len_source, incomplete) = if payload_len == 0 && (false == header_rest.is_empty()) { (header_rest, LenSource::Slice, false) } else if payload_len > header_rest.len() { (header_rest, LenSource::Slice, true) } else { unsafe { ( core::slice::from_raw_parts(header_rest.as_ptr(), payload_len), LenSource::Ipv6HeaderPayloadLen, false, ) } }; // read ipv6 extensions headers let (exts, next_header, exts_rest, mut stop_err) = Ipv6Extensions::from_slice_lax(header.next_header, header_payload); use err::ipv6_exts::HeaderSliceError as I; if let Some((I::Len(err), _)) = stop_err.as_mut() { err.layer_start_offset += Ipv6Header::LEN; err.len_source = len_source; }; let fragmented = exts.is_fragmenting_payload(); Ok(( IpHeaders::Ipv6(header, exts), LaxIpPayloadSlice { incomplete, ip_number: next_header, fragmented, len_source, payload: exts_rest, }, stop_err, )) } /// Reads an IP (v4 or v6) header from the current position (requires /// crate feature `std`). #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read( reader: &mut T, ) -> Result<(IpHeaders, IpNumber), err::ip::HeaderReadError> { use crate::io::LimitedReader; use err::ip::{HeaderError::*, HeaderReadError::*, HeadersError::*}; let value = { let mut buf = [0; 1]; reader.read_exact(&mut buf).map_err(Io)?; buf[0] }; match value >> 4 { 4 => { // get internet header length let ihl = value & 0xf; // check that the ihl is correct if ihl < 5 { return Err(Content(Ip(Ipv4HeaderLengthSmallerThanHeader { ihl }))); } // read the rest of the header let header_len_u16 = u16::from(ihl) * 4; let header_len = usize::from(header_len_u16); let mut buffer = [0u8; Ipv4Header::MAX_LEN]; buffer[0] = value; reader.read_exact(&mut buffer[1..header_len]).map_err(Io)?; let header = unsafe { // SAFETY: Safe as both the IHL and slice len have been verified Ipv4HeaderSlice::from_slice_unchecked(&buffer[..header_len]) } .to_header(); // check that the total len is long enough to contain the header let total_len = usize::from(header.total_len); let mut reader = if total_len < header_len { return Err(Len(LenError { required_len: header_len, len: total_len, len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::Ipv4Packet, layer_start_offset: 0, })); } else { // create a new reader that is limited by the total_len value length LimitedReader::new( reader, total_len - header_len, LenSource::Ipv4HeaderTotalLen, header_len, Layer::Ipv4Header, ) }; // read the extension headers if present Ipv4Extensions::read_limited(&mut reader, header.protocol) .map(|(ext, next)| (IpHeaders::Ipv4(header, ext), next)) .map_err(|err| { use err::ip_auth::HeaderLimitedReadError as I; match err { I::Io(err) => Io(err), I::Len(err) => Len(err), I::Content(err) => Content(Ipv4Ext(err)), } }) } 6 => { let header = Ipv6Header::read_without_version(reader, value & 0xf).map_err(Io)?; // create a new reader that is limited by the payload_len value length let mut reader = LimitedReader::new( reader, header.payload_length.into(), LenSource::Ipv6HeaderPayloadLen, header.header_len(), Layer::Ipv6Header, ); Ipv6Extensions::read_limited(&mut reader, header.next_header) .map(|(ext, next)| (IpHeaders::Ipv6(header, ext), next)) .map_err(|err| { use err::ipv6_exts::HeaderLimitedReadError as I; match err { I::Io(err) => Io(err), I::Len(err) => Len(err), I::Content(err) => Content(Ipv6Ext(err)), } }) } version_number => Err(Content(Ip(UnsupportedIpVersion { version_number }))), } } /// Writes an IP (v4 or v6) header to the current position (requires /// crate feature `std`). #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn write( &self, writer: &mut T, ) -> Result<(), HeadersWriteError> { use crate::IpHeaders::*; use HeadersWriteError::*; match *self { Ipv4(ref header, ref extensions) => { header.write(writer).map_err(Io)?; extensions.write(writer, header.protocol).map_err(|err| { use err::ipv4_exts::HeaderWriteError as I; match err { I::Io(err) => Io(err), I::Content(err) => Ipv4Exts(err), } }) } Ipv6(ref header, ref extensions) => { header.write(writer).map_err(Io)?; extensions.write(writer, header.next_header).map_err(|err| { use err::ipv6_exts::HeaderWriteError as I; match err { I::Io(err) => Io(err), I::Content(err) => Ipv6Exts(err), } }) } } } /// Returns the size when the ip header & extensions are serialized pub fn header_len(&self) -> usize { use crate::IpHeaders::*; match *self { Ipv4(ref header, ref extensions) => header.header_len() + extensions.header_len(), Ipv6(_, ref extensions) => Ipv6Header::LEN + extensions.header_len(), } } /// Returns the last next header number following the ip header /// and header extensions. pub fn next_header(&self) -> Result { use crate::err::ip_exts::ExtsWalkError::*; use crate::IpHeaders::*; match *self { Ipv4(ref header, ref extensions) => { extensions.next_header(header.protocol).map_err(Ipv4Exts) } Ipv6(ref header, ref extensions) => { extensions.next_header(header.next_header).map_err(Ipv6Exts) } } } /// Sets all the next_header fields in the ipv4 & ipv6 header /// as well as in all extension headers and returns the ether /// type number. /// /// The given number will be set as the last "next_header" or /// protocol number. pub fn set_next_headers(&mut self, last_next_header: IpNumber) -> u16 { use IpHeaders::*; match self { Ipv4(ref mut header, ref mut extensions) => { header.protocol = extensions.set_next_headers(last_next_header); EtherType::IPV4.0 } Ipv6(ref mut header, ref mut extensions) => { header.next_header = extensions.set_next_headers(last_next_header); EtherType::IPV4.0 } } } /// Tries to set the length field in the ip header given the length of data /// after the ip header and extension header(s). /// /// If the payload length is too large to be stored in the length fields /// of the ip header an error is returned. /// /// Note that this function will automatically add the length of the extension /// headers is they are present. pub fn set_payload_len(&mut self, len: usize) -> Result<(), ValueTooBigError> { use crate::err::ValueType; match self { IpHeaders::Ipv4(ipv4_hdr, exts) => { if let Some(complete_len) = len.checked_add(exts.header_len()) { ipv4_hdr.set_payload_len(complete_len) } else { Err(ValueTooBigError { actual: len, max_allowed: usize::from(u16::MAX) - ipv4_hdr.header_len() - exts.header_len(), value_type: ValueType::Ipv4PayloadLength, }) } } IpHeaders::Ipv6(ipv6_hdr, exts) => { if let Some(complete_len) = len.checked_add(exts.header_len()) { ipv6_hdr.set_payload_length(complete_len) } else { Err(ValueTooBigError { actual: len, max_allowed: usize::from(u16::MAX) - exts.header_len(), value_type: ValueType::Ipv4PayloadLength, }) } } } } /// Returns true if the payload is fragmented based on the IPv4 header /// or the IPv6 fragment header. pub fn is_fragmenting_payload(&self) -> bool { match self { IpHeaders::Ipv4(ipv4, _) => ipv4.is_fragmenting_payload(), IpHeaders::Ipv6(_, exts) => exts.is_fragmenting_payload(), } } } #[cfg(test)] mod test { use crate::{ err::{ ip::{HeadersError, HeadersSliceError}, Layer, LenError, }, ip_number::*, test_gens::*, *, }; use alloc::{borrow::ToOwned, format, vec::Vec}; use proptest::prelude::*; use std::io::Cursor; const EXTENSION_KNOWN_IP_NUMBERS: [IpNumber; 5] = [ AUTH, IPV6_DEST_OPTIONS, IPV6_HOP_BY_HOP, IPV6_FRAG, IPV6_ROUTE, ]; fn combine_v4(v4: &Ipv4Header, ext: &Ipv4Extensions, payload: &[u8]) -> IpHeaders { IpHeaders::Ipv4( { let mut v4 = v4.clone(); v4.protocol = if ext.auth.is_some() { AUTH } else { UDP }; v4.total_len = (v4.header_len() + ext.header_len() + payload.len()) as u16; v4.header_checksum = v4.calc_header_checksum(); v4 }, ext.clone(), ) } fn combine_v6(v6: &Ipv6Header, ext: &Ipv6Extensions, payload: &[u8]) -> IpHeaders { let (ext, next_header) = { let mut ext = ext.clone(); let next_header = ext.set_next_headers(UDP); (ext, next_header) }; IpHeaders::Ipv6( { let mut v6 = v6.clone(); v6.next_header = next_header; v6.payload_length = (ext.header_len() + payload.len()) as u16; v6 }, ext, ) } proptest! { #[test] fn debug( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { assert_eq!( format!( "Ipv4({:?}, {:?})", v4, v4_exts ), format!("{:?}", IpHeaders::Ipv4(v4, v4_exts)) ); assert_eq!( format!( "Ipv6({:?}, {:?})", v6, v6_exts ), format!("{:?}", IpHeaders::Ipv6(v6, v6_exts)) ); } } proptest! { #[test] fn clone_eq( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { { let v4 = IpHeaders::Ipv4(v4, v4_exts); assert_eq!(v4, v4.clone()); } { let v6 = IpHeaders::Ipv6(v6, v6_exts); assert_eq!(v6, v6.clone()); } } } proptest! { #[test] fn ipv4( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { assert_eq!( IpHeaders::Ipv4(v4.clone(), v4_exts.clone()).ipv4(), Some((&v4, &v4_exts)) ); assert_eq!( IpHeaders::Ipv6(v6.clone(), v6_exts.clone()).ipv4(), None ); } } proptest! { #[test] fn ipv6( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { assert_eq!( IpHeaders::Ipv4(v4.clone(), v4_exts.clone()).ipv6(), None ); assert_eq!( IpHeaders::Ipv6(v6.clone(), v6_exts.clone()).ipv6(), Some((&v6, &v6_exts)) ); } } proptest! { #[test] #[allow(deprecated)] fn read_from_slice( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), ) { let header = combine_v4(&v4, &v4_exts, &[]); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let actual = IpHeaders::read_from_slice(&buffer).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); assert_eq!(actual.2, &buffer[buffer.len()..]); } } proptest! { #[test] fn ip_from_slice( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { use err::ip::{HeadersError::*, HeaderError::*, HeadersSliceError::*}; // empty error assert_eq!( IpHeaders::from_slice(&[]), Err(Len(err::LenError { required_len: 1, len: 0, len_source: LenSource::Slice, layer: err::Layer::IpHeader, layer_start_offset: 0, })) ); // unknown version for version_number in 0..=0xfu8 { if version_number != 4 && version_number != 6 { assert_eq!( IpHeaders::from_slice(&[version_number << 4]), Err(Content(Ip(UnsupportedIpVersion { version_number }))) ); } } let payload = [1,2,3,4]; // v4 { let header = combine_v4(&v4, &v4_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // read { let actual = IpHeaders::from_slice(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, IpPayloadSlice{ ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv4HeaderTotalLen, payload: &payload } ); } // read error ipv4 header IpHeaders::from_slice(&buffer[..1]).unwrap_err(); // read error ipv4 extensions if v4_exts.header_len() > 0 { IpHeaders::from_slice(&buffer[..v4.header_len() + 1]).unwrap_err(); } // total length smaller the header { let bad_total_len = (v4.header_len() - 1) as u16; let mut buffer = buffer.clone(); // inject bad total_len let bad_total_len_be = bad_total_len.to_be_bytes(); buffer[2] = bad_total_len_be[0]; buffer[3] = bad_total_len_be[1]; assert_eq!( IpHeaders::from_slice(&buffer[..]).unwrap_err(), HeadersSliceError::Len(LenError{ required_len: v4.header_len(), len: bad_total_len as usize, len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::Ipv4Packet, layer_start_offset: 0, }) ); } } // v6 { let header = combine_v6(&v6, &v6_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // len error { let actual = IpHeaders::from_slice(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, IpPayloadSlice{ ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv6HeaderPayloadLen, payload: &payload } ); } // read error header IpHeaders::from_slice(&buffer[..1]).unwrap_err(); // read error ipv4 extensions if v6_exts.header_len() > 0 { IpHeaders::from_slice(&buffer[..Ipv6Header::LEN + 1]).unwrap_err(); } // len error (with payload len zero) if v6_exts.header_len() > 0 { let mut buffer = buffer.clone(); // inject zero as payload len buffer[4] = 0; buffer[5] = 0; assert!( IpHeaders::from_slice( &buffer[..buffer.len() - payload.len() - 2] ).is_err() ); } } } } proptest! { #[test] fn from_slice_lax( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { use err::ip::{HeaderError::*, LaxHeaderSliceError::*}; let payload = [1,2,3,4]; // empty error assert_eq!( IpHeaders::from_slice_lax(&[]), Err(Len(err::LenError { required_len: 1, len: 0, len_source: LenSource::Slice, layer: err::Layer::IpHeader, layer_start_offset: 0, })) ); // unknown version for version_number in 0..=0xfu8 { if version_number != 4 && version_number != 6 { assert_eq!( IpHeaders::from_slice_lax(&[version_number << 4]), Err(Content(UnsupportedIpVersion { version_number })) ); } } // v4 { let header = combine_v4(&v4, &v4_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // normal read { let actual = IpHeaders::from_slice_lax(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv4HeaderTotalLen, payload: &payload } ); } // error len smaller then min header len for len in 1..Ipv4Header::MIN_LEN { assert_eq!( IpHeaders::from_slice_lax(&buffer[..len]), Err(Len(err::LenError { required_len: Ipv4Header::MIN_LEN, len, len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })) ); } // ihl value error { let mut bad_ihl_buffer = buffer.clone(); for bad_ihl in 0..5 { bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl; assert_eq!( IpHeaders::from_slice_lax(&bad_ihl_buffer), Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl: bad_ihl })) ); } } // ihl len error for short_ihl in 5..usize::from(v4.ihl()) { assert_eq!( IpHeaders::from_slice_lax(&buffer[..4*short_ihl]), Err(Len(err::LenError { required_len: usize::from(v4.ihl())*4, len: 4*short_ihl, len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })) ); } // total_len bigger then slice len (fallback to slice len) for payload_len in 0..payload.len(){ let actual = IpHeaders::from_slice_lax(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: true, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &payload[..payload_len] } ); } // len error ipv4 extensions if v4_exts.header_len() > 0 { let (_, _, stop_err) = IpHeaders::from_slice_lax(&buffer[..v4.header_len() + 1]).unwrap(); assert!(stop_err.is_some()); } // content error ipv4 extensions if v4_exts.auth.is_some() { use err::ip_auth::HeaderError::ZeroPayloadLen; use err::ip_exts::HeadersSliceError::Content; use err::ip_exts::HeaderError::Ipv4Ext; // introduce a auth header zero payload error let mut errored_buffer = buffer.clone(); // inject length zero into auth header (not valid, will // trigger a content error) errored_buffer[v4.header_len() + 1] = 0; let (_, _, stop_err) = IpHeaders::from_slice_lax(&errored_buffer).unwrap(); assert_eq!(stop_err, Some((Content(Ipv4Ext(ZeroPayloadLen)), Layer::IpAuthHeader))); } // total length smaller the header (fallback to slice len) { let bad_total_len = (v4.header_len() - 1) as u16; let mut buffer = buffer.clone(); // inject bad total_len let bad_total_len_be = bad_total_len.to_be_bytes(); buffer[2] = bad_total_len_be[0]; buffer[3] = bad_total_len_be[1]; let actual = IpHeaders::from_slice_lax(&buffer[..]).unwrap(); let (v4_header, v4_exts) = header.ipv4().unwrap(); let expected_headers = IpHeaders::Ipv4( { let mut expected_v4 = v4_header.clone(); expected_v4.total_len = bad_total_len; expected_v4 }, v4_exts.clone() ); assert_eq!(&expected_headers, &actual.0); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &buffer[v4_header.header_len() + v4_exts.header_len()..], } ); } } // v6 { let header = combine_v6(&v6, &v6_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // normal read { let actual = IpHeaders::from_slice_lax(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv6HeaderPayloadLen, payload: &payload } ); } // smaller then header for len in 1..Ipv6Header::LEN { assert_eq!( IpHeaders::from_slice_lax(&buffer[..len]), Err(Len(err::LenError { required_len: Ipv6Header::LEN, len, len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, })) ); } // extension len error if v6_exts.header_len() > 0 { let (actual, _, stop_err) = IpHeaders::from_slice_lax(&buffer[..v6.header_len() + 1]).unwrap(); assert_eq!(&actual.ipv6().as_ref().unwrap().0, &header.ipv6().as_ref().unwrap().0); assert!(stop_err.is_some()); } // extension content error if v6_exts.auth.is_some() { use err::ip_auth::HeaderError::ZeroPayloadLen; use err::ip_exts::HeadersSliceError::Content; use err::ip_exts::HeaderError::Ipv6Ext; use err::ipv6_exts::HeaderError::IpAuth; // introduce a auth header zero payload error let mut errored_buffer = buffer.clone(); let auth_offset = v6.header_len() + v6_exts.hop_by_hop_options.as_ref().map(|h| h.header_len()).unwrap_or(0) + v6_exts.destination_options.as_ref().map(|h| h.header_len()).unwrap_or(0) + v6_exts.routing.as_ref().map(|h| h.routing.header_len()).unwrap_or(0) + // routing.final_destination_options skiped, as after auth v6_exts.fragment.as_ref().map(|h| h.header_len()).unwrap_or(0); // inject length zero into auth header (not valid, will // trigger a content error) errored_buffer[auth_offset + 1] = 0; let (_, _, stop_err) = IpHeaders::from_slice_lax(&errored_buffer).unwrap(); assert_eq!( stop_err, Some((Content(Ipv6Ext(IpAuth(ZeroPayloadLen))), Layer::IpAuthHeader)) ); } // slice smaller then payload len for len in (v6.header_len()+v6_exts.header_len())..buffer.len() - 1 { let actual = IpHeaders::from_slice_lax(&buffer[..len]).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: true, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &payload[..len - v6.header_len() - v6_exts.header_len()] } ); } // payload len zero (fallback to slice len) { let mut buffer = buffer.clone(); // inject zero as payload len buffer[4] = 0; buffer[5] = 0; let actual = IpHeaders::from_slice_lax(&buffer[..]).unwrap(); let (v6_header, v6_exts) = header.ipv6().unwrap(); let expected_headers = IpHeaders::Ipv6( { let mut expected_v6 = v6_header.clone(); expected_v6.payload_length = 0; expected_v6 }, v6_exts.clone() ); assert_eq!(&expected_headers, &actual.0); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &buffer[v6_header.header_len() + v6_exts.header_len()..], } ); } } } } proptest! { #[test] fn from_ipv4_slice( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), ) { let payload = [1,2,3,4]; let header = combine_v4(&v4, &v4_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // read { let actual = IpHeaders::from_ipv4_slice(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, IpPayloadSlice{ ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv4HeaderTotalLen, payload: &payload } ); } // read error ipv4 header IpHeaders::from_ipv4_slice(&buffer[..1]).unwrap_err(); // read error ipv4 extensions if v4_exts.header_len() > 0 { IpHeaders::from_ipv4_slice(&buffer[..v4.header_len() + 1]).unwrap_err(); } // total length smaller the header { let bad_total_len = (v4.header_len() - 1) as u16; let mut buffer = buffer.clone(); // inject bad total_len let bad_total_len_be = bad_total_len.to_be_bytes(); buffer[2] = bad_total_len_be[0]; buffer[3] = bad_total_len_be[1]; assert_eq!( IpHeaders::from_ipv4_slice(&buffer[..]).unwrap_err(), err::ipv4::SliceError::Len(LenError{ required_len: v4.header_len(), len: bad_total_len as usize, len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::Ipv4Packet, layer_start_offset: 0, }) ); } } } proptest! { #[test] fn from_ipv4_slice_lax( v4 in ipv4_any(), v4_exts in ipv4_extensions_any() ) { use err::ip::{LaxHeaderSliceError::*, HeaderError::*}; let payload = [1,2,3,4]; // empty error assert_eq!( IpHeaders::from_ipv4_slice_lax(&[]), Err(Len(err::LenError { required_len: 20, len: 0, len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })) ); // build a buffer with a valid packet let header = combine_v4(&v4, &v4_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // normal read { let actual = IpHeaders::from_ipv4_slice_lax(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv4HeaderTotalLen, payload: &payload } ); } // error len smaller then min header len for len in 1..Ipv4Header::MIN_LEN { assert_eq!( IpHeaders::from_ipv4_slice_lax(&buffer[..len]), Err(Len(err::LenError { required_len: Ipv4Header::MIN_LEN, len, len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })) ); } // ihl value error { let mut bad_ihl_buffer = buffer.clone(); for bad_ihl in 0..5 { bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl; assert_eq!( IpHeaders::from_ipv4_slice_lax(&bad_ihl_buffer), Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl: bad_ihl })) ); } } // ihl len error for short_ihl in 5..usize::from(v4.ihl()) { assert_eq!( IpHeaders::from_ipv4_slice_lax(&buffer[..4*short_ihl]), Err(Len(err::LenError { required_len: usize::from(v4.ihl())*4, len: 4*short_ihl, len_source: LenSource::Slice, layer: err::Layer::Ipv4Header, layer_start_offset: 0, })) ); } // total_len bigger then slice len (fallback to slice len) for payload_len in 0..payload.len(){ let actual = IpHeaders::from_ipv4_slice_lax(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: true, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &payload[..payload_len] } ); } // len error ipv4 extensions if v4_exts.header_len() > 0 { let (actual, _, stop_err) = IpHeaders::from_ipv4_slice_lax(&buffer[..v4.header_len() + 1]).unwrap(); assert_eq!(actual.ipv4().unwrap().0, header.ipv4().unwrap().0); assert!(stop_err.is_some()); } // content error ipv4 extensions if v4_exts.auth.is_some() { use err::ip_auth::HeaderSliceError::Content; use err::ip_auth::HeaderError::ZeroPayloadLen; // introduce a auth header zero payload error let mut errored_buffer = buffer.clone(); // inject length zero into auth header (not valid, will // trigger a content error) errored_buffer[v4.header_len() + 1] = 0; let (_, _, stop_err) = IpHeaders::from_ipv4_slice_lax(&errored_buffer).unwrap(); assert_eq!(stop_err, Some(Content(ZeroPayloadLen))); } // total length smaller the header (fallback to slice len) { let bad_total_len = (v4.header_len() - 1) as u16; let mut buffer = buffer.clone(); // inject bad total_len let bad_total_len_be = bad_total_len.to_be_bytes(); buffer[2] = bad_total_len_be[0]; buffer[3] = bad_total_len_be[1]; let actual = IpHeaders::from_ipv4_slice_lax(&buffer[..]).unwrap(); let (v4_header, v4_exts) = header.ipv4().unwrap(); let expected_headers = IpHeaders::Ipv4( { let mut expected_v4 = v4_header.clone(); expected_v4.total_len = bad_total_len; expected_v4 }, v4_exts.clone() ); assert_eq!(&expected_headers, &actual.0); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &buffer[v4_header.header_len() + v4_exts.header_len()..], } ); } } } proptest! { #[test] fn from_ipv6_slice( v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { let payload = [1,2,3,4]; let header = combine_v6(&v6, &v6_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // len error { let actual = IpHeaders::from_ipv6_slice(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, IpPayloadSlice{ ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv6HeaderPayloadLen, payload: &payload } ); } // read error header IpHeaders::from_ipv6_slice(&buffer[..1]).unwrap_err(); // read error ipv4 extensions if v6_exts.header_len() > 0 { IpHeaders::from_ipv6_slice(&buffer[..Ipv6Header::LEN + 1]).unwrap_err(); } // len error (with payload len zero) if v6_exts.header_len() > 0 { let mut buffer = buffer.clone(); // inject zero as payload len buffer[4] = 0; buffer[5] = 0; assert!( IpHeaders::from_ipv6_slice( &buffer[..buffer.len() - payload.len() - 2] ).is_err() ); } } } proptest! { #[test] fn from_ipv6_slice_lax( v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), bad_version in 0..0xfu8 ) { use err::ipv6::{HeaderError::*, HeaderSliceError::*}; let payload = [1,2,3,4]; // empty error assert_eq!( IpHeaders::from_ipv6_slice_lax(&[]), Err(Len(err::LenError { required_len: Ipv6Header::LEN, len: 0, len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, })) ); // setup buffer with a valid packet let header = combine_v6(&v6, &v6_exts, &payload); let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1); header.write(&mut buffer).unwrap(); buffer.extend_from_slice(&payload); buffer.push(1); // add some value to check the return slice // unknown version if bad_version != 6 { let mut bad_vers_buffer = buffer.clone(); bad_vers_buffer[0] = (bad_vers_buffer[0] & 0xf) | (bad_version << 4); assert_eq!( IpHeaders::from_ipv6_slice_lax(&bad_vers_buffer), Err(Content(UnexpectedVersion { version_number: bad_version })) ); } // normal read { let actual = IpHeaders::from_ipv6_slice_lax(&buffer).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Ipv6HeaderPayloadLen, payload: &payload } ); } // smaller then header for len in 1..Ipv6Header::LEN { assert_eq!( IpHeaders::from_ipv6_slice_lax(&buffer[..len]), Err(Len(err::LenError { required_len: Ipv6Header::LEN, len, len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, })) ); } // extension len error if v6_exts.header_len() > 0 { let (_, _, err) = IpHeaders::from_ipv6_slice_lax(&buffer[..v6.header_len() + 1]).unwrap(); assert!(err.is_some()); } // extension content error if v6_exts.auth.is_some() { use err::ip_auth::HeaderError::ZeroPayloadLen; use err::ipv6_exts::{HeaderSliceError::Content, HeaderError::IpAuth}; // introduce a auth header zero payload error let mut errored_buffer = buffer.clone(); let auth_offset = v6.header_len() + v6_exts.hop_by_hop_options.as_ref().map(|h| h.header_len()).unwrap_or(0) + v6_exts.destination_options.as_ref().map(|h| h.header_len()).unwrap_or(0) + v6_exts.routing.as_ref().map(|h| h.routing.header_len()).unwrap_or(0) + // routing.final_destination_options skiped, as after auth v6_exts.fragment.as_ref().map(|h| h.header_len()).unwrap_or(0); // inject length zero into auth header (not valid, will // trigger a content error) errored_buffer[auth_offset + 1] = 0; let (_, _, err) = IpHeaders::from_ipv6_slice_lax(&errored_buffer).unwrap(); assert_eq!(err, Some((Content(IpAuth(ZeroPayloadLen)), Layer::IpAuthHeader))); } // slice smaller then payload len for len in (v6.header_len()+v6_exts.header_len())..buffer.len() - 1 { let actual = IpHeaders::from_ipv6_slice_lax(&buffer[..len]).unwrap(); assert_eq!(&actual.0, &header); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: true, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &payload[..len - v6.header_len() - v6_exts.header_len()] } ); } // payload len zero (fallback to slice len) { let mut buffer = buffer.clone(); // inject zero as payload len buffer[4] = 0; buffer[5] = 0; let actual = IpHeaders::from_ipv6_slice_lax(&buffer[..]).unwrap(); let (v6_header, v6_exts) = header.ipv6().unwrap(); let expected_headers = IpHeaders::Ipv6( { let mut expected_v6 = v6_header.clone(); expected_v6.payload_length = 0; expected_v6 }, v6_exts.clone() ); assert_eq!(&expected_headers, &actual.0); assert_eq!( actual.1, LaxIpPayloadSlice{ incomplete: false, ip_number: header.next_header().unwrap(), fragmented: header.is_fragmenting_payload(), len_source: LenSource::Slice, payload: &buffer[v6_header.header_len() + v6_exts.header_len()..], } ); } } } proptest! { #[test] fn read( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), bad_ihl in 0u8..5u8, v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { use err::ip::{HeadersError::*, HeaderError::*}; // no data error { let mut cursor = Cursor::new(&[]); assert!( IpHeaders::read(&mut cursor) .unwrap_err() .io() .is_some() ); } // version error { let mut cursor = Cursor::new(&[0xf << 4]); assert_eq!( IpHeaders::read(&mut cursor).unwrap_err().content().unwrap(), Ip(UnsupportedIpVersion { version_number: 0xf }) ); } // v4 { let header = combine_v4(&v4, &v4_exts, &[]); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); // read { let mut cursor = Cursor::new(&buffer[..]); let actual = IpHeaders::read(&mut cursor).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); } // read error ihl smaller then header { let mut buffer = buffer.clone(); // inject bad ihl buffer[0] = (buffer[0] & 0b1111_0000) | bad_ihl; let mut cursor = Cursor::new(&buffer[..]); assert_eq!( IpHeaders::read(&mut cursor) .unwrap_err() .content() .unwrap(), Ip(Ipv4HeaderLengthSmallerThanHeader{ ihl: bad_ihl }) ); } // total length smaller the header { let bad_total_len = (v4.header_len() - 1) as u16; let mut buffer = buffer.clone(); // inject bad total_len let bad_total_len_be = bad_total_len.to_be_bytes(); buffer[2] = bad_total_len_be[0]; buffer[3] = bad_total_len_be[1]; let mut cursor = Cursor::new(&buffer[..]); assert_eq!( IpHeaders::read(&mut cursor) .unwrap_err() .len() .unwrap(), LenError{ required_len: v4.header_len(), len: bad_total_len as usize, len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::Ipv4Packet, layer_start_offset: 0, } ); } // read len error ipv4 { let mut cursor = Cursor::new(&buffer[..1]); assert!( IpHeaders::read(&mut cursor) .unwrap_err() .io() .is_some() ); } // read error ipv4 extensions if v4_exts.header_len() > 0 { let mut cursor = Cursor::new(&buffer[..v4.header_len() + 1]); IpHeaders::read(&mut cursor).unwrap_err(); } // len error in extensions if v4_exts.auth.is_some() { let bad_total_len = (buffer.len() - 1) as u16; let mut buffer = buffer.clone(); // inject bad total_len let bad_total_len_be = bad_total_len.to_be_bytes(); buffer[2] = bad_total_len_be[0]; buffer[3] = bad_total_len_be[1]; let mut cursor = Cursor::new(&buffer[..]); assert_eq!( IpHeaders::read(&mut cursor) .unwrap_err() .len() .unwrap(), LenError{ required_len: buffer.len() - v4.header_len(), len: bad_total_len as usize - v4.header_len(), len_source: LenSource::Ipv4HeaderTotalLen, layer: Layer::IpAuthHeader, layer_start_offset: v4.header_len(), } ); } // extension content error if v4_exts.auth.is_some() { let mut buffer = buffer.clone(); // inject zero as header len buffer[v4.header_len() + 1] = 0; let mut cursor = Cursor::new(&buffer[..]); assert_eq!( IpHeaders::read(&mut cursor) .unwrap_err() .content() .unwrap(), HeadersError::Ipv4Ext( err::ip_auth::HeaderError::ZeroPayloadLen ) ); } } // v6 { let header = combine_v6(&v6, &v6_exts, &[]); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); // ok case { let mut cursor = Cursor::new(&buffer[..]); let actual = IpHeaders::read(&mut cursor).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); } // io error in v6 header section { let mut cursor = Cursor::new(&buffer[..1]); assert!( IpHeaders::read(&mut cursor).unwrap_err().io().is_some() ); } // io error ipv6 extensions if v6_exts.header_len() > 0 { let mut cursor = Cursor::new(&buffer[..Ipv6Header::LEN + 1]); assert!( IpHeaders::read(&mut cursor).unwrap_err().io().is_some() ); } // len error in ipv6 extensions if v6_exts.header_len() > 0 { // inject an invalid length let mut buffer = buffer.clone(); let bad_payload_len = (buffer.len() - header.header_len()) as u16; let bad_payload_len_be = bad_payload_len.to_be_bytes(); buffer[4] = bad_payload_len_be[0]; buffer[5] = bad_payload_len_be[1]; // expect a length error let mut cursor = Cursor::new(&buffer[..]); assert!( IpHeaders::read(&mut cursor).unwrap_err().len().is_some() ); } // extension content error if let Some(auth) = v6_exts.auth.as_ref() { // only do it if auth is the last header if v6_exts.routing.is_none() { // inject zero as header len let mut buffer = buffer.clone(); let auth_offset = buffer.len() - auth.header_len(); buffer[auth_offset + 1] = 0; let mut cursor = Cursor::new(&buffer[..]); assert_eq!( IpHeaders::read(&mut cursor) .unwrap_err() .content() .unwrap(), HeadersError::Ipv6Ext(err::ipv6_exts::HeaderError::IpAuth( err::ip_auth::HeaderError::ZeroPayloadLen )) ); } } } } } proptest! { #[test] fn write( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { // v4 { let header = combine_v4(&v4, &v4_exts, &[]); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let actual = IpHeaders::from_slice(&buffer).unwrap().0; assert_eq!(header, actual); // write error v4 header { let mut buffer = [0u8;1]; let mut cursor = Cursor::new(&mut buffer[..]); assert!( header.write(&mut cursor) .unwrap_err() .io() .is_some() ); } // write io error v4 extension headers if v4_exts.header_len() > 0 { let mut buffer = [0u8;Ipv4Header::MAX_LEN + 1]; let mut cursor = Cursor::new(&mut buffer[..v4.header_len() + 1]); assert!( header.write(&mut cursor) .unwrap_err() .io() .is_some() ); } // write content error v4 extension headers if v4_exts.header_len() > 0 { // cause a missing reference error let header = IpHeaders::Ipv4( { let mut v4 = v4.clone(); // skips extension header v4.protocol = ip_number::UDP; v4.total_len = (v4.header_len() + v4_exts.header_len()) as u16; v4.header_checksum = v4.calc_header_checksum(); v4 }, v4_exts.clone(), ); let mut buffer = [0u8;Ipv4Header::MAX_LEN + IpAuthHeader::MAX_LEN]; let mut cursor = Cursor::new(&mut buffer[..]); assert!(header.write(&mut cursor).is_err()); } } // v6 { let header = combine_v6(&v6, &v6_exts, &[]); // normal write let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let actual = IpHeaders::from_slice(&buffer).unwrap().0; assert_eq!(header, actual); // write error v6 header { let mut buffer = [0u8;1]; let mut cursor = Cursor::new(&mut buffer[..]); assert!( header.write(&mut cursor) .unwrap_err() .io() .is_some() ); } // write error v6 extension headers if v6_exts.header_len() > 0 { let mut buffer = [0u8;Ipv6Header::LEN + 1]; let mut cursor = Cursor::new(&mut buffer[..]); assert!( header.write(&mut cursor) .unwrap_err() .io() .is_some() ); } // write content error v4 extension headers if v6_exts.header_len() > 0 { // cause a missing reference error let header = IpHeaders::Ipv6( { let mut v6 = v6.clone(); // skips extension header v6.next_header = ip_number::UDP; v6.payload_length = v6_exts.header_len() as u16; v6 }, v6_exts.clone(), ); let mut buffer = [0u8;Ipv4Header::MAX_LEN + IpAuthHeader::MAX_LEN]; let mut cursor = Cursor::new(&mut buffer[..]); assert!(header.write(&mut cursor).is_err()); } } } } proptest! { #[test] fn header_len( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { assert_eq!( v4.header_len() + v4_exts.header_len(), IpHeaders::Ipv4(v4, v4_exts).header_len() ); assert_eq!( Ipv6Header::LEN + v6_exts.header_len(), IpHeaders::Ipv6(v6, v6_exts).header_len() ); } } proptest! { #[test] fn next_header( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), post_header in ip_number_any() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { { let mut header = v4.clone(); let mut exts = v4_exts.clone(); header.protocol = exts.set_next_headers(post_header); assert_eq!( Ok(post_header), IpHeaders::Ipv4(header, exts).next_header() ); } { let mut header = v6.clone(); let mut exts = v6_exts.clone(); header.next_header = exts.set_next_headers(post_header); assert_eq!( Ok(post_header), IpHeaders::Ipv6(header, exts).next_header() ); } } } // TODO set_next_headers proptest! { #[test] fn set_payload_len( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), payload_len in 0usize..10 ) { // ipv4 (with valid payload length) { let mut actual = IpHeaders::Ipv4( v4.clone(), v4_exts.clone() ); actual.set_payload_len(payload_len).unwrap(); assert_eq!( actual, IpHeaders::Ipv4( { let mut re = v4.clone(); re.set_payload_len(v4_exts.header_len() + payload_len).unwrap(); re }, v4_exts.clone() ) ); } // ipv6 (with valid payload length) { let mut actual = IpHeaders::Ipv6( v6.clone(), v6_exts.clone() ); actual.set_payload_len(payload_len).unwrap(); assert_eq!( actual, IpHeaders::Ipv6( { let mut re = v6.clone(); re.set_payload_length(v6_exts.header_len() + payload_len).unwrap(); re }, v6_exts.clone() ) ); } // v4 (with invalid size) { let mut actual = IpHeaders::Ipv4( v4.clone(), v4_exts.clone() ); assert!(actual.set_payload_len(usize::MAX).is_err()); } // v6 (with invalid size) { let mut actual = IpHeaders::Ipv6( v6.clone(), v6_exts.clone() ); assert!(actual.set_payload_len(usize::MAX).is_err()); } } } proptest! { #[test] fn is_fragmenting_payload( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any() ) { // ipv4 assert_eq!( v4.is_fragmenting_payload(), IpHeaders::Ipv4(v4.clone(), v4_exts.clone()).is_fragmenting_payload() ); // ipv6 assert_eq!( v6_exts.is_fragmenting_payload(), IpHeaders::Ipv6(v6.clone(), v6_exts.clone()).is_fragmenting_payload() ); } } }