| use crate::{ | |
| err::{packet::SliceError, Layer, LenError}, | |
| *, | |
| }; | |
| /// Decoded packet headers (data link layer and lower) with lax length checks. | |
| /// | |
| /// You can use | |
| /// | |
| /// * [`LaxPacketHeaders::from_ethernet`] | |
| /// * [`LaxPacketHeaders::from_ether_type`] | |
| /// * [`LaxPacketHeaders::from_ip`] | |
| /// | |
| /// depending on your starting header to parse the headers in a slice and get this | |
| /// struct as a result. | |
| #[derive(Clone, Debug, Eq, PartialEq)] | |
| pub struct LaxPacketHeaders<'a> { | |
| /// Ethernet II header if present. | |
| pub link: Option<LinkHeader>, | |
| /// Single or double vlan headers if present. | |
| pub vlan: Option<VlanHeader>, | |
| /// IPv4 or IPv6 header and IP extension headers if present. | |
| pub net: Option<NetHeaders>, | |
| /// TCP or UDP header if present. | |
| pub transport: Option<TransportHeader>, | |
| /// Payload of the last parsed layer. | |
| pub payload: LaxPayloadSlice<'a>, | |
| /// Error that stopped the parsing and the layer on which the stop occurred. | |
| pub stop_err: Option<(err::packet::SliceError, Layer)>, | |
| } | |
| impl<'a> LaxPacketHeaders<'a> { | |
| /// Separates a network packet into different headers from the ethernet header | |
| /// downwards with lax length checks and non-terminating errors. | |
| /// | |
| /// # Example | |
| /// | |
| /// Basic usage: | |
| /// | |
| ///``` | |
| /// # use etherparse::{Ethernet2Header, PacketBuilder}; | |
| /// # let builder = PacketBuilder:: | |
| /// # ethernet2([1,2,3,4,5,6], //source mac | |
| /// # [7,8,9,10,11,12]) //destionation mac | |
| /// # .ipv4([192,168,1,1], //source ip | |
| /// # [192,168,1,2], //destination ip | |
| /// # 20) //time to life | |
| /// # .udp(21, //source port | |
| /// # 1234); //desitnation port | |
| /// # // payload of the udp packet | |
| /// # let payload = [1,2,3,4,5,6,7,8]; | |
| /// # // get some memory to store the serialized data | |
| /// # let mut packet = Vec::<u8>::with_capacity( | |
| /// # builder.size(payload.len()) | |
| /// # ); | |
| /// # builder.write(&mut packet, &payload).unwrap(); | |
| /// # | |
| /// use etherparse::{ether_type, LaxPacketHeaders, LenSource, LaxPayloadSlice}; | |
| /// | |
| /// match LaxPacketHeaders::from_ethernet(&packet) { | |
| /// Err(value) => { | |
| /// // An error is returned in case the ethernet II header could | |
| /// // not be parsed (other errors are stored in the "stop_err" field) | |
| /// println!("Err {:?}", value) | |
| /// }, | |
| /// Ok(value) => { | |
| /// if let Some((stop_err, error_layer)) = value.stop_err.as_ref() { | |
| /// // error was encountered after parsing the ethernet 2 header | |
| /// println!("Error on layer {}: {:?}", error_layer, stop_err); | |
| /// } | |
| /// | |
| /// // parts that could be parsed without error | |
| /// println!("link: {:?}", value.link); | |
| /// println!("vlan: {:?}", value.vlan); | |
| /// println!("net: {:?}", value.net); | |
| /// println!("transport: {:?}", value.transport); | |
| /// | |
| /// // net (ip) & transport (udp or tcp) | |
| /// println!("net: {:?}", value.net); | |
| /// match value.payload { | |
| /// LaxPayloadSlice::Ether(e) => { | |
| /// println!("ether payload (ether type {:?}): {:?}", e.ether_type, e.payload); | |
| /// } | |
| /// LaxPayloadSlice::Ip(ip) => { | |
| /// println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload); | |
| /// if ip.incomplete { | |
| /// println!(" IP payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// if ip.fragmented { | |
| /// println!(" IP payload fragmented"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Udp{ payload, incomplete } => { | |
| /// println!("UDP payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" UDP payload incomplete (length in UDP or IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Tcp{ payload, incomplete } => { | |
| /// println!("TCP payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" TCP payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Icmpv4{ payload, incomplete } => { | |
| /// println!("Icmpv4 payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" Icmpv4 payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Icmpv6{ payload, incomplete } => { | |
| /// println!("Icmpv6 payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" Icmpv6 payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// | |
| /// ``` | |
| pub fn from_ethernet(slice: &'a [u8]) -> Result<LaxPacketHeaders<'a>, err::LenError> { | |
| let (ethernet, rest) = Ethernet2Header::from_slice(slice)?; | |
| let mut result = Self::from_ether_type(ethernet.ether_type, rest); | |
| result.link = Some(LinkHeader::Ethernet2(ethernet)); | |
| if let Some((SliceError::Len(l), _)) = result.stop_err.as_mut() { | |
| l.layer_start_offset += Ethernet2Header::LEN; | |
| } | |
| Ok(result) | |
| } | |
| /// Separates a network packet into different headers using | |
| /// the given `ether_type` number to identify the first header with lax length | |
| /// checks and non-terminating errors. | |
| /// | |
| /// The result is returned as a [`LaxSlicedPacket`] struct. Currently supported | |
| /// ether type numbers are: | |
| /// | |
| /// * `ether_type::IPV4` | |
| /// * `ether_type::IPV6` | |
| /// * `ether_type::VLAN_TAGGED_FRAME` | |
| /// * `ether_type::PROVIDER_BRIDGING` | |
| /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME` | |
| /// | |
| /// If an unsupported ether type is given the given slice will be set as payload | |
| /// and all other fields will be set to `None`. | |
| /// | |
| /// # Example | |
| /// | |
| /// Basic usage: | |
| /// | |
| ///``` | |
| /// # use etherparse::{Ethernet2Header, PacketBuilder}; | |
| /// # let builder = PacketBuilder:: | |
| /// # ethernet2([1,2,3,4,5,6], //source mac | |
| /// # [7,8,9,10,11,12]) //destionation mac | |
| /// # .ipv4([192,168,1,1], //source ip | |
| /// # [192,168,1,2], //destination ip | |
| /// # 20) //time to life | |
| /// # .udp(21, //source port | |
| /// # 1234); //desitnation port | |
| /// # // payload of the udp packet | |
| /// # let payload = [1,2,3,4,5,6,7,8]; | |
| /// # // get some memory to store the serialized data | |
| /// # let mut complete_packet = Vec::<u8>::with_capacity( | |
| /// # builder.size(payload.len()) | |
| /// # ); | |
| /// # builder.write(&mut complete_packet, &payload).unwrap(); | |
| /// # // skip ethernet 2 header so we can parse from there downwards | |
| /// # let packet = &complete_packet[Ethernet2Header::LEN..]; | |
| /// # | |
| /// use etherparse::{ether_type, LaxPacketHeaders, LenSource, LaxPayloadSlice}; | |
| /// | |
| /// let value = LaxPacketHeaders::from_ether_type(ether_type::IPV4, &packet); | |
| /// | |
| /// if let Some((stop_err, error_layer)) = value.stop_err.as_ref() { | |
| /// // error was encountered after parsing the ethernet 2 header | |
| /// println!("Error on layer {}: {:?}", error_layer, stop_err); | |
| /// } | |
| /// | |
| /// // link is unfilled | |
| /// assert_eq!(value.link, None); | |
| /// | |
| /// // parts that could be parsed without error | |
| /// println!("vlan: {:?}", value.vlan); | |
| /// println!("net: {:?}", value.net); | |
| /// println!("transport: {:?}", value.transport); | |
| /// | |
| /// // net (ip) & transport (udp or tcp) | |
| /// println!("net: {:?}", value.net); | |
| /// match value.payload { | |
| /// LaxPayloadSlice::Ether(e) => { | |
| /// println!("ether payload (ether type {:?}): {:?}", e.ether_type, e.payload); | |
| /// } | |
| /// LaxPayloadSlice::Ip(ip) => { | |
| /// println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload); | |
| /// if ip.incomplete { | |
| /// println!(" IP payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// if ip.fragmented { | |
| /// println!(" IP payload fragmented"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Udp{ payload, incomplete } => { | |
| /// println!("UDP payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" UDP payload incomplete (length in UDP or IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Tcp{ payload, incomplete } => { | |
| /// println!("TCP payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" TCP payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Icmpv4{ payload, incomplete } => { | |
| /// println!("Icmpv4 payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" Icmpv4 payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Icmpv6{ payload, incomplete } => { | |
| /// println!("Icmpv6 payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" Icmpv6 payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// } | |
| /// ``` | |
| pub fn from_ether_type(mut ether_type: EtherType, slice: &'a [u8]) -> LaxPacketHeaders<'a> { | |
| use err::packet::SliceError::*; | |
| let mut rest = slice; | |
| let mut offset = 0; | |
| let mut result = LaxPacketHeaders { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| payload: LaxPayloadSlice::Ether(EtherPayloadSlice { | |
| ether_type, | |
| payload: rest, | |
| }), | |
| stop_err: None, | |
| }; | |
| // parse vlan header(s) | |
| use ether_type::*; | |
| result.vlan = match ether_type { | |
| VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { | |
| use crate::VlanHeader::*; | |
| let (outer, outer_rest) = match SingleVlanHeader::from_slice(rest) { | |
| Ok(value) => value, | |
| Err(err) => { | |
| result.stop_err = Some((Len(err), Layer::VlanHeader)); | |
| return result; | |
| } | |
| }; | |
| // set the rest & ether_type for the following operations | |
| rest = outer_rest; | |
| offset += SingleVlanHeader::LEN; | |
| ether_type = outer.ether_type; | |
| result.payload = LaxPayloadSlice::Ether(EtherPayloadSlice { | |
| ether_type, | |
| payload: rest, | |
| }); | |
| // parse second vlan header if present | |
| match ether_type { | |
| // second vlan tagging header | |
| VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { | |
| let (inner, inner_rest) = match SingleVlanHeader::from_slice(rest) { | |
| Ok(value) => value, | |
| Err(mut err) => { | |
| err.layer_start_offset += SingleVlanHeader::LEN; | |
| result.vlan = Some(VlanHeader::Single(outer.clone())); | |
| result.stop_err = Some((Len(err), Layer::VlanHeader)); | |
| return result; | |
| } | |
| }; | |
| // set the rest & ether_type for the following operations | |
| rest = inner_rest; | |
| offset += SingleVlanHeader::LEN; | |
| ether_type = inner.ether_type; | |
| result.payload = LaxPayloadSlice::Ether(EtherPayloadSlice { | |
| ether_type, | |
| payload: rest, | |
| }); | |
| Some(Double(DoubleVlanHeader { outer, inner })) | |
| } | |
| // no second vlan header detected -> single vlan header | |
| _ => Some(Single(outer)), | |
| } | |
| } | |
| // no vlan header | |
| _ => None, | |
| }; | |
| // parse ip | |
| match ether_type { | |
| IPV4 | IPV6 => match result.add_ip(offset, rest) { | |
| Ok(_) => {} | |
| Err(err) => { | |
| use err::ip::LaxHeaderSliceError as I; | |
| result.stop_err = Some(match err { | |
| I::Len(mut l) => { | |
| l.layer_start_offset += offset; | |
| (Len(l), Layer::IpHeader) | |
| } | |
| I::Content(c) => (Ip(c), Layer::IpHeader), | |
| }); | |
| return result; | |
| } | |
| }, | |
| _ => {} | |
| }; | |
| result | |
| } | |
| /// Separates a network packet slice into different headers from the | |
| /// ip header downwards with lax length checks and will still return | |
| /// a result even if an error is encountered in a layer (except IP). | |
| /// | |
| /// This function has two main differences to [`PacketHeaders::from_ip_slice`]: | |
| /// | |
| /// * Errors encountered bellow the IpHeader 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. | |
| /// * Length in the IP header & UDP headers are allowed to be inconsistent with the | |
| /// given slice length (e.g. data is missing from the slice). In this case it falls | |
| /// back to the length of slice. See [`LaxIpSlice::from_slice`] for a detailed | |
| /// description of when the slice length is used as a fallback. | |
| /// | |
| /// The result is returned as a [`SlicedPacket`] struct. This function | |
| /// assumes the given data starts with an IPv4 or IPv6 header. | |
| /// | |
| /// # Examples | |
| /// | |
| /// Basic usage: | |
| /// | |
| ///``` | |
| /// # use etherparse::{PacketBuilder, Ethernet2Header}; | |
| /// # let builder = PacketBuilder:: | |
| /// # ipv4([192,168,1,1], //source ip | |
| /// # [192,168,1,2], //destination ip | |
| /// # 20) //time to life | |
| /// # .udp(21, //source port | |
| /// # 1234); //desitnation port | |
| /// # //payload of the udp packet | |
| /// # let payload = [1,2,3,4,5,6,7,8]; | |
| /// # // get some memory to store the serialized data | |
| /// # let mut complete_packet = Vec::<u8>::with_capacity( | |
| /// # builder.size(payload.len()) | |
| /// # ); | |
| /// # builder.write(&mut complete_packet, &payload).unwrap(); | |
| /// # // skip ethernet 2 header so we can parse from there downwards | |
| /// # let packet = &complete_packet[Ethernet2Header::LEN..]; | |
| /// # | |
| /// use etherparse::{ether_type, LaxPacketHeaders, LenSource, LaxPayloadSlice}; | |
| /// | |
| /// match LaxPacketHeaders::from_ip(&packet) { | |
| /// Err(value) => { | |
| /// // An error is returned in case the ip header could | |
| /// // not be parsed (other errors are stored in the "stop_err" field) | |
| /// println!("Err {:?}", value) | |
| /// }, | |
| /// Ok(value) => { | |
| /// if let Some((stop_err, error_layer)) = value.stop_err.as_ref() { | |
| /// // error was encountered after parsing the ethernet 2 header | |
| /// println!("Error on layer {}: {:?}", error_layer, stop_err); | |
| /// } | |
| /// | |
| /// // link & vlan is unfilled | |
| /// assert_eq!(value.link, None); | |
| /// assert_eq!(value.vlan, None); | |
| /// | |
| /// // parts that could be parsed without error | |
| /// println!("net: {:?}", value.net); | |
| /// println!("transport: {:?}", value.transport); | |
| /// | |
| /// // net (ip) & transport (udp or tcp) | |
| /// println!("net: {:?}", value.net); | |
| /// match value.payload { | |
| /// // if you parse from IP down there will be no ether payload | |
| /// LaxPayloadSlice::Ether(e) => unreachable!(), | |
| /// LaxPayloadSlice::Ip(ip) => { | |
| /// println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload); | |
| /// if ip.incomplete { | |
| /// println!(" IP payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// if ip.fragmented { | |
| /// println!(" IP payload fragmented"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Udp{ payload, incomplete } => { | |
| /// println!("UDP payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" UDP payload incomplete (length in UDP or IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Tcp{ payload, incomplete } => { | |
| /// println!("TCP payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" TCP payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Icmpv4{ payload, incomplete } => { | |
| /// println!("Icmpv4 payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" Icmpv4 payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// LaxPayloadSlice::Icmpv6{ payload, incomplete } => { | |
| /// println!("Icmpv6 payload: {:?}", payload); | |
| /// if incomplete { | |
| /// println!(" Icmpv6 payload incomplete (length in IP header indicated more data should be present)"); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// | |
| /// ``` | |
| pub fn from_ip(slice: &'a [u8]) -> Result<LaxPacketHeaders<'a>, err::ip::LaxHeaderSliceError> { | |
| let mut result = Self { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| // dummy initialize (will be overwritten if add_ip is successfull) | |
| payload: LaxPayloadSlice::Udp { | |
| payload: &[], | |
| incomplete: true, | |
| }, | |
| stop_err: None, | |
| }; | |
| result.add_ip(0, slice)?; | |
| Ok(result) | |
| } | |
| fn add_ip( | |
| &mut self, | |
| offset: usize, | |
| slice: &'a [u8], | |
| ) -> Result<(), err::ip::LaxHeaderSliceError> { | |
| use err::packet::SliceError::*; | |
| // read ipv4 header & extensions and payload slice | |
| let (ip, ip_payload, stop_err) = IpHeaders::from_slice_lax(slice)?; | |
| // set the next | |
| self.net = Some(ip.into()); | |
| self.payload = LaxPayloadSlice::Ip(ip_payload.clone()); | |
| // if a stop error was encountered return it | |
| if let Some((err, layer)) = stop_err { | |
| use err::ip_exts::HeaderError as IC; | |
| use err::ip_exts::HeadersSliceError as I; | |
| self.stop_err = Some(( | |
| match err { | |
| I::Len(mut l) => { | |
| l.layer_start_offset += offset; | |
| l.len_source = ip_payload.len_source; | |
| Len(l) | |
| } | |
| I::Content(e) => match e { | |
| IC::Ipv4Ext(e) => SliceError::Ipv4Exts(e), | |
| IC::Ipv6Ext(e) => SliceError::Ipv6Exts(e), | |
| }, | |
| }, | |
| layer, | |
| )); | |
| return Ok(()); | |
| } | |
| // update the offset with the ip headers | |
| let offset = offset + ((ip_payload.payload.as_ptr() as usize) - (slice.as_ptr() as usize)); | |
| // decode transport layer | |
| if false == ip_payload.fragmented { | |
| // helper function to set the len source in len errors | |
| let add_len_source = |mut len_error: LenError| -> err::packet::SliceError { | |
| // only change the len source if the lower layer has not set it | |
| if LenSource::Slice == len_error.len_source { | |
| len_error.len_source = ip_payload.len_source; | |
| len_error.layer_start_offset += offset; | |
| } | |
| err::packet::SliceError::Len(len_error) | |
| }; | |
| use crate::ip_number::*; | |
| use err::tcp::HeaderSliceError::*; | |
| match ip_payload.ip_number { | |
| ICMP => match Icmpv4Slice::from_slice(ip_payload.payload) { | |
| Ok(i) => { | |
| self.transport = Some(TransportHeader::Icmpv4(i.header())); | |
| self.payload = LaxPayloadSlice::Icmpv4 { | |
| payload: i.payload(), | |
| incomplete: ip_payload.incomplete, | |
| }; | |
| } | |
| Err(e) => { | |
| self.stop_err = Some((add_len_source(e), Layer::Icmpv4)); | |
| } | |
| }, | |
| IPV6_ICMP => match Icmpv6Slice::from_slice(ip_payload.payload) { | |
| Ok(i) => { | |
| self.transport = Some(TransportHeader::Icmpv6(i.header())); | |
| self.payload = LaxPayloadSlice::Icmpv6 { | |
| payload: i.payload(), | |
| incomplete: ip_payload.incomplete, | |
| }; | |
| } | |
| Err(e) => { | |
| self.stop_err = Some((add_len_source(e), Layer::Icmpv6)); | |
| } | |
| }, | |
| UDP => { | |
| match UdpSlice::from_slice_lax(ip_payload.payload) { | |
| Ok(u) => { | |
| self.transport = Some(TransportHeader::Udp(u.to_header())); | |
| self.payload = LaxPayloadSlice::Udp { | |
| payload: u.payload(), | |
| // TODO also check the udp header length | |
| incomplete: ip_payload.incomplete, | |
| }; | |
| } | |
| Err(e) => { | |
| self.stop_err = Some((add_len_source(e), Layer::UdpHeader)); | |
| } | |
| } | |
| } | |
| TCP => match TcpHeader::from_slice(ip_payload.payload) { | |
| Ok(t) => { | |
| self.transport = Some(TransportHeader::Tcp(t.0)); | |
| self.payload = LaxPayloadSlice::Tcp { | |
| payload: t.1, | |
| incomplete: ip_payload.incomplete, | |
| }; | |
| } | |
| Err(e) => match e { | |
| Len(l) => { | |
| self.stop_err = Some((add_len_source(l), Layer::TcpHeader)); | |
| } | |
| Content(c) => { | |
| self.stop_err = Some((SliceError::Tcp(c), Layer::TcpHeader)); | |
| } | |
| }, | |
| }, | |
| _ => {} | |
| } | |
| } | |
| Ok(()) | |
| } | |
| } | |
| #[cfg(test)] | |
| mod test { | |
| use super::*; | |
| use crate::test_packet::TestPacket; | |
| const VLAN_ETHER_TYPES: [EtherType; 3] = [ | |
| ether_type::VLAN_TAGGED_FRAME, | |
| ether_type::PROVIDER_BRIDGING, | |
| ether_type::VLAN_DOUBLE_TAGGED_FRAME, | |
| ]; | |
| #[test] | |
| fn clone_eq() { | |
| let header = LaxPacketHeaders { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| stop_err: None, | |
| payload: LaxPayloadSlice::Udp { | |
| payload: &[], | |
| incomplete: false, | |
| }, | |
| }; | |
| assert_eq!(header.clone(), header); | |
| } | |
| #[test] | |
| fn debug() { | |
| use alloc::format; | |
| let payload = LaxPayloadSlice::Udp { | |
| payload: &[], | |
| incomplete: false, | |
| }; | |
| let header = LaxPacketHeaders { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| payload: payload.clone(), | |
| stop_err: None, | |
| }; | |
| assert_eq!( | |
| format!("{:?}", header), | |
| format!( | |
| "LaxPacketHeaders {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?}, payload: {:?}, stop_err: {:?} }}", | |
| header.link, header.vlan, header.net, header.transport, payload, header.stop_err | |
| ) | |
| ); | |
| } | |
| #[test] | |
| fn from_x_slice() { | |
| // no eth | |
| from_x_slice_vlan_variants(&TestPacket { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }); | |
| // eth | |
| { | |
| let eth = Ethernet2Header { | |
| source: [1, 2, 3, 4, 5, 6], | |
| destination: [1, 2, 3, 4, 5, 6], | |
| ether_type: 0.into(), | |
| }; | |
| let test = TestPacket { | |
| link: Some(LinkHeader::Ethernet2(eth.clone())), | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }; | |
| // ok ethernet header (with unknown next) | |
| from_x_slice_vlan_variants(&test); | |
| // eth len error | |
| { | |
| let data = test.to_vec(&[]); | |
| for len in 0..data.len() { | |
| assert_test_result(&test, &[], &data[..len], None, None); | |
| } | |
| } | |
| } | |
| // unknown ether_type | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let actual = LaxPacketHeaders::from_ether_type(0.into(), &payload); | |
| assert_eq!(None, actual.link); | |
| assert_eq!(None, actual.vlan); | |
| assert_eq!(None, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert_eq!( | |
| actual.payload, | |
| LaxPayloadSlice::Ether(EtherPayloadSlice { | |
| ether_type: 0.into(), | |
| payload: &payload | |
| }) | |
| ); | |
| assert_eq!(None, actual.stop_err); | |
| } | |
| } | |
| fn from_x_slice_vlan_variants(base: &TestPacket) { | |
| // none | |
| from_x_slice_ip_variants(base); | |
| // single vlan header | |
| { | |
| let single = SingleVlanHeader { | |
| pcp: 1.try_into().unwrap(), | |
| drop_eligible_indicator: false, | |
| vlan_id: 2.try_into().unwrap(), | |
| ether_type: 3.into(), | |
| }; | |
| for vlan_ether_type in VLAN_ETHER_TYPES { | |
| let mut test = base.clone(); | |
| test.set_ether_type(vlan_ether_type); | |
| test.vlan = Some(VlanHeader::Single(single.clone())); | |
| // ok vlan header | |
| from_x_slice_ip_variants(&test); | |
| // len error | |
| { | |
| let data = test.to_vec(&[]); | |
| for len in 0..single.header_len() { | |
| let base_len = test.len(&[]) - single.header_len(); | |
| let err = LenError { | |
| required_len: single.header_len(), | |
| len, | |
| len_source: LenSource::Slice, | |
| layer: Layer::VlanHeader, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::VlanHeader)), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| // double vlan header | |
| for outer_vlan_ether_type in VLAN_ETHER_TYPES { | |
| for inner_vlan_ether_type in VLAN_ETHER_TYPES { | |
| let double = DoubleVlanHeader { | |
| outer: SingleVlanHeader { | |
| pcp: 1.try_into().unwrap(), | |
| drop_eligible_indicator: false, | |
| vlan_id: 2.try_into().unwrap(), | |
| ether_type: inner_vlan_ether_type, | |
| }, | |
| inner: SingleVlanHeader { | |
| pcp: 1.try_into().unwrap(), | |
| drop_eligible_indicator: false, | |
| vlan_id: 2.try_into().unwrap(), | |
| ether_type: 3.into(), | |
| }, | |
| }; | |
| let mut test = base.clone(); | |
| test.set_ether_type(outer_vlan_ether_type); | |
| test.vlan = Some(VlanHeader::Double(double.clone())); | |
| // ok double vlan header | |
| from_x_slice_ip_variants(&test); | |
| // len error | |
| { | |
| let data = test.to_vec(&[]); | |
| for len in 0..SingleVlanHeader::LEN { | |
| let base_len = test.len(&[]) - SingleVlanHeader::LEN; | |
| let err = LenError { | |
| required_len: SingleVlanHeader::LEN, | |
| len, | |
| len_source: LenSource::Slice, | |
| layer: Layer::VlanHeader, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::VlanHeader)), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| fn from_x_slice_ip_variants(base: &TestPacket) { | |
| // none | |
| from_x_slice_transport_variants(base); | |
| // ipv4 | |
| for fragmented in [false, true] { | |
| let ipv4 = { | |
| let mut ipv4 = | |
| Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]).unwrap(); | |
| ipv4.more_fragments = fragmented; | |
| ipv4 | |
| }; | |
| { | |
| let mut test = base.clone(); | |
| test.set_ether_type(ether_type::IPV4); | |
| test.net = Some(NetHeaders::Ipv4(ipv4.clone(), Default::default())); | |
| test.set_payload_len(0); | |
| // ok ipv4 | |
| from_x_slice_transport_variants(&test); | |
| // ipv4 len error | |
| { | |
| let data = test.to_vec(&[]); | |
| for len in 0..ipv4.header_len() { | |
| let base_len = test.len(&[]) - ipv4.header_len(); | |
| let err = LenError { | |
| required_len: if len < 1 { 1 } else { ipv4.header_len() }, | |
| len, | |
| len_source: LenSource::Slice, | |
| layer: if len < 1 { | |
| Layer::IpHeader | |
| } else { | |
| Layer::Ipv4Header | |
| }, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| Some(err::ip::LaxHeaderSliceError::Len(err.clone())), | |
| Some((SliceError::Len(err.clone()), Layer::IpHeader)), | |
| ); | |
| } | |
| } | |
| // ipv4 content error (ihl length too small) | |
| { | |
| use err::ip::HeaderError::*; | |
| let mut data = test.to_vec(&[]); | |
| let ipv4_offset = data.len() - ipv4.header_len(); | |
| // set the ihl to 0 to trigger a content error | |
| data[ipv4_offset] = 0b1111_0000 & data[ipv4_offset]; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| Some(err::ip::LaxHeaderSliceError::Content( | |
| Ipv4HeaderLengthSmallerThanHeader { ihl: 0 }, | |
| )), | |
| Some(( | |
| SliceError::Ip(Ipv4HeaderLengthSmallerThanHeader { ihl: 0 }), | |
| Layer::IpHeader, | |
| )), | |
| ); | |
| } | |
| // ipv 4total length too small (does not change the output) | |
| { | |
| let mut data = test.to_vec(&[]); | |
| let ipv4_offset = data.len() - ipv4.header_len(); | |
| // set the total length to 0 to trigger a content error | |
| data[ipv4_offset + 2] = 0; | |
| data[ipv4_offset + 3] = 0; | |
| let mut mod_test = test.clone(); | |
| mod_test.net = Some({ | |
| let (h, e) = test.net.as_ref().map(|v| v.ipv4_ref()).flatten().unwrap(); | |
| let mut ipv4 = h.clone(); | |
| ipv4.total_len = 0; | |
| NetHeaders::Ipv4(ipv4, e.clone()) | |
| }); | |
| assert_test_result(&mod_test, &[], &data, None, None); | |
| } | |
| } | |
| // ipv4 extension content error | |
| { | |
| let auth = IpAuthHeader::new(0.into(), 1, 2, &[]).unwrap(); | |
| let mut test = base.clone(); | |
| test.set_ether_type(ether_type::IPV4); | |
| test.net = Some(NetHeaders::Ipv4( | |
| { | |
| let mut ipv4 = ipv4.clone(); | |
| ipv4.protocol = ip_number::AUTH; | |
| ipv4 | |
| }, | |
| Ipv4Extensions { | |
| auth: Some(auth.clone()), | |
| }, | |
| )); | |
| test.set_payload_len(0); | |
| // ok ipv4 & extension | |
| from_x_slice_transport_variants(&test); | |
| // ipv4 extension len error | |
| for len in 0..auth.header_len() { | |
| // set payload length | |
| let mut test = test.clone(); | |
| test.set_payload_le_from_ip_on( | |
| -1 * (auth.header_len() as isize) + (len as isize), | |
| ); | |
| let data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - auth.header_len(); | |
| let err = LenError { | |
| required_len: auth.header_len(), | |
| len, | |
| len_source: LenSource::Ipv4HeaderTotalLen, | |
| layer: Layer::IpAuthHeader, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::IpAuthHeader)), | |
| ); | |
| } | |
| // ipv4 extension content error | |
| { | |
| let mut data = test.to_vec(&[]); | |
| let auth_offset = data.len() - auth.header_len(); | |
| // set the icv len too smaller then allowed | |
| data[auth_offset + 1] = 0; | |
| // expect an error | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| None, | |
| Some(( | |
| SliceError::Ipv4Exts(err::ip_auth::HeaderError::ZeroPayloadLen), | |
| Layer::IpAuthHeader, | |
| )), | |
| ); | |
| } | |
| } | |
| } | |
| // ipv6 | |
| { | |
| let ipv6 = Ipv6Header { | |
| traffic_class: 0, | |
| flow_label: 1.try_into().unwrap(), | |
| payload_length: 2, | |
| next_header: 3.into(), | |
| hop_limit: 4, | |
| source: [0; 16], | |
| destination: [0; 16], | |
| }; | |
| // ipv6 header only | |
| { | |
| let mut test = base.clone(); | |
| test.set_ether_type(ether_type::IPV6); | |
| test.net = Some(NetHeaders::Ipv6(ipv6.clone(), Default::default())); | |
| test.set_payload_len(0); | |
| // ok ipv6 | |
| from_x_slice_transport_variants(&test); | |
| // header len ipv6 | |
| { | |
| let data = test.to_vec(&[]); | |
| for len in 0..ipv6.header_len() { | |
| let base_len = test.len(&[]) - ipv6.header_len(); | |
| let err = err::LenError { | |
| required_len: if len < 1 { 1 } else { ipv6.header_len() }, | |
| len, | |
| len_source: LenSource::Slice, | |
| layer: if len < 1 { | |
| Layer::IpHeader | |
| } else { | |
| Layer::Ipv6Header | |
| }, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| Some(err::ip::LaxHeaderSliceError::Len(err.clone())), | |
| Some(( | |
| SliceError::Len({ | |
| if len < 1 { | |
| let mut err = err.clone(); | |
| err.required_len = 1; | |
| err.layer = Layer::IpHeader; | |
| err | |
| } else { | |
| err.clone() | |
| } | |
| }), | |
| Layer::IpHeader, | |
| )), | |
| ); | |
| } | |
| } | |
| // content error ipv6 | |
| { | |
| use err::ip::{HeaderError::*, LaxHeaderSliceError::Content}; | |
| let mut data = test.to_vec(&[]); | |
| // inject an invalid ip version | |
| let base_len = data.len() - ipv6.header_len(); | |
| data[base_len] = data[base_len] & 0b0000_1111; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| Some(Content(UnsupportedIpVersion { version_number: 0 })), | |
| Some(( | |
| SliceError::Ip(UnsupportedIpVersion { version_number: 0 }), | |
| Layer::IpHeader, | |
| )), | |
| ); | |
| } | |
| } | |
| // ipv6 + extension | |
| for fragment in [false, true] { | |
| let auth = IpAuthHeader::new(ip_number::GGP, 1, 2, &[]).unwrap(); | |
| let frag = Ipv6FragmentHeader { | |
| next_header: ip_number::AUTH, | |
| fragment_offset: 0.try_into().unwrap(), | |
| more_fragments: fragment, | |
| identification: 3, | |
| }; | |
| let mut test = base.clone(); | |
| test.set_ether_type(ether_type::IPV6); | |
| test.net = Some(NetHeaders::Ipv6( | |
| { | |
| let mut ipv6 = ipv6.clone(); | |
| ipv6.next_header = ip_number::IPV6_FRAG; | |
| ipv6 | |
| }, | |
| { | |
| let mut exts: Ipv6Extensions = Default::default(); | |
| exts.fragment = Some(frag.clone()); | |
| exts.auth = Some(auth.clone()); | |
| exts | |
| }, | |
| )); | |
| test.set_payload_len(0); | |
| // ok ipv6 & extensions | |
| from_x_slice_transport_variants(&test); | |
| // ipv6 extension len error | |
| for len in 0..auth.header_len() { | |
| // set payload length | |
| let mut test = test.clone(); | |
| test.set_payload_le_from_ip_on( | |
| -1 * (auth.header_len() as isize) + (len as isize), | |
| ); | |
| let data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - auth.header_len(); | |
| let err = LenError { | |
| required_len: auth.header_len(), | |
| len, | |
| len_source: LenSource::Ipv6HeaderPayloadLen, | |
| layer: Layer::IpAuthHeader, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::IpAuthHeader)), | |
| ); | |
| } | |
| // ipv6 extension content error (auth) | |
| { | |
| let mut data = test.to_vec(&[]); | |
| let auth_offset = data.len() - auth.header_len(); | |
| // set the icv len too smaller then allowed | |
| data[auth_offset + 1] = 0; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| None, | |
| Some(( | |
| SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth( | |
| err::ip_auth::HeaderError::ZeroPayloadLen, | |
| )), | |
| Layer::IpAuthHeader, | |
| )), | |
| ); | |
| } | |
| // ipv6 extension content error (hop by hop not at start) | |
| { | |
| let mut data = test.to_vec(&[]); | |
| let auth_offset = data.len() - auth.header_len(); | |
| // set the next header to be a hop-by-hop header to trigger a "not at start error" | |
| data[auth_offset] = 0; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| None, | |
| Some(( | |
| SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart), | |
| Layer::Ipv6HopByHopHeader, | |
| )), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| fn from_x_slice_transport_variants(base: &TestPacket) { | |
| // none | |
| from_x_slice_assert_ok(base); | |
| // transport can only be set if ip is present | |
| if let Some(ip) = &base.net { | |
| // udp | |
| { | |
| let udp = UdpHeader { | |
| source_port: 1, | |
| destination_port: 2, | |
| length: 3, | |
| checksum: 4, | |
| }; | |
| let mut test = base.clone(); | |
| test.net = Some({ | |
| let mut ip = match ip { | |
| NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()), | |
| NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()), | |
| }; | |
| ip.set_next_headers(ip_number::UDP); | |
| ip.into() | |
| }); | |
| test.transport = Some(TransportHeader::Udp(udp.clone())); | |
| test.set_payload_len(0); | |
| // ok decode | |
| from_x_slice_assert_ok(&test); | |
| // length error | |
| if false == test.is_ip_payload_fragmented() { | |
| for len in 0..udp.header_len() { | |
| // build new test packet | |
| let mut test = test.clone(); | |
| // set payload length | |
| test.set_payload_le_from_ip_on(len as isize); | |
| // generate data | |
| let data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - udp.header_len(); | |
| let err = LenError { | |
| required_len: udp.header_len(), | |
| len, | |
| len_source: match test.net.as_ref().unwrap() { | |
| NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen, | |
| NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen, | |
| }, | |
| layer: Layer::UdpHeader, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::UdpHeader)), | |
| ); | |
| } | |
| } | |
| } | |
| // tcp | |
| { | |
| let tcp = TcpHeader::new(1, 2, 3, 4); | |
| let mut test = base.clone(); | |
| test.net = Some({ | |
| let mut ip = match ip { | |
| NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()), | |
| NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()), | |
| }; | |
| ip.set_next_headers(ip_number::TCP); | |
| ip.into() | |
| }); | |
| test.transport = Some(TransportHeader::Tcp(tcp.clone())); | |
| test.set_payload_len(0); | |
| // ok decode | |
| from_x_slice_assert_ok(&test); | |
| // error can only occur if ip does not fragment the packet | |
| if false == test.is_ip_payload_fragmented() { | |
| // length error | |
| { | |
| for len in 0..(tcp.header_len() as usize) { | |
| // set payload length | |
| let mut test = test.clone(); | |
| test.set_payload_le_from_ip_on(len as isize); | |
| let data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - (tcp.header_len() as usize); | |
| let err = LenError { | |
| required_len: tcp.header_len() as usize, | |
| len, | |
| len_source: match test.net.as_ref().unwrap() { | |
| NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen, | |
| NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen, | |
| }, | |
| layer: Layer::TcpHeader, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::TcpHeader)), | |
| ); | |
| } | |
| } | |
| // content error | |
| { | |
| let mut data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - (tcp.header_len() as usize); | |
| // set data offset to 0 to trigger an error | |
| data[base_len + 12] = data[base_len + 12] & 0b0000_1111; | |
| let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 0 }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data, | |
| None, | |
| Some((SliceError::Tcp(err.clone()), Layer::TcpHeader)), | |
| ); | |
| } | |
| } | |
| } | |
| // icmpv4 | |
| { | |
| let icmpv4 = | |
| Icmpv4Header::new(Icmpv4Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 })); | |
| let mut test = base.clone(); | |
| test.net = Some({ | |
| let mut ip = match ip { | |
| NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()), | |
| NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()), | |
| }; | |
| ip.set_next_headers(ip_number::ICMP); | |
| ip.into() | |
| }); | |
| test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone())); | |
| test.set_payload_len(0); | |
| // ok decode | |
| from_x_slice_assert_ok(&test); | |
| // length error | |
| if false == test.is_ip_payload_fragmented() { | |
| for len in 0..icmpv4.header_len() { | |
| // set payload length | |
| let mut test = test.clone(); | |
| test.set_payload_le_from_ip_on(len as isize); | |
| let data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - icmpv4.header_len(); | |
| let err = LenError { | |
| required_len: icmpv4.header_len(), | |
| len, | |
| len_source: match test.net.as_ref().unwrap() { | |
| NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen, | |
| NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen, | |
| }, | |
| layer: Layer::Icmpv4, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::Icmpv4)), | |
| ); | |
| } | |
| } | |
| } | |
| // icmpv6 | |
| { | |
| let icmpv6 = | |
| Icmpv6Header::new(Icmpv6Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 })); | |
| let mut test = base.clone(); | |
| test.net = Some({ | |
| let mut ip = match ip { | |
| NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()), | |
| NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()), | |
| }; | |
| ip.set_next_headers(ip_number::IPV6_ICMP); | |
| ip.into() | |
| }); | |
| test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone())); | |
| test.set_payload_len(0); | |
| // ok decode | |
| from_x_slice_assert_ok(&test); | |
| // length error | |
| if false == test.is_ip_payload_fragmented() { | |
| for len in 0..icmpv6.header_len() { | |
| // set payload length | |
| let mut test = test.clone(); | |
| test.set_payload_le_from_ip_on(len as isize); | |
| let data = test.to_vec(&[]); | |
| let base_len = test.len(&[]) - icmpv6.header_len(); | |
| let err = LenError { | |
| required_len: icmpv6.header_len(), | |
| len, | |
| len_source: match test.net.as_ref().unwrap() { | |
| NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen, | |
| NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen, | |
| }, | |
| layer: Layer::Icmpv6, | |
| layer_start_offset: base_len, | |
| }; | |
| assert_test_result( | |
| &test, | |
| &[], | |
| &data[..base_len + len], | |
| None, | |
| Some((SliceError::Len(err.clone()), Layer::Icmpv6)), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| fn from_x_slice_assert_ok(test_base: &TestPacket) { | |
| // setup payload | |
| let payload = [1, 2, 3, 4]; | |
| // set length fields in ip headers | |
| let test = { | |
| let mut test = test_base.clone(); | |
| test.set_payload_len(payload.len()); | |
| test | |
| }; | |
| // write data | |
| let data = test.to_vec(&payload); | |
| assert_test_result(&test, &payload, &data, None, None); | |
| } | |
| /// Check that the given output & errors (if present) are generated based on the given | |
| /// input. | |
| fn assert_test_result( | |
| test: &TestPacket, | |
| expected_payload: &[u8], | |
| data: &[u8], | |
| expected_ip_err: Option<err::ip::LaxHeaderSliceError>, | |
| expected_stop_err: Option<(SliceError, Layer)>, | |
| ) { | |
| fn compare_vlan(test: &TestPacket, data: &[u8], actual: &LaxPacketHeaders) { | |
| let vlan_offset = if let Some(e) = test.link.as_ref() { | |
| e.header_len() | |
| } else { | |
| 0 | |
| }; | |
| match test.vlan.as_ref() { | |
| Some(VlanHeader::Double(d)) => { | |
| if data.len() >= vlan_offset + DoubleVlanHeader::LEN { | |
| assert_eq!(test.vlan, actual.vlan); | |
| } else if data.len() >= vlan_offset + SingleVlanHeader::LEN { | |
| assert_eq!(Some(VlanHeader::Single(d.outer.clone())), actual.vlan); | |
| } else { | |
| assert_eq!(None, actual.vlan); | |
| } | |
| } | |
| Some(VlanHeader::Single(s)) => { | |
| if data.len() >= vlan_offset + SingleVlanHeader::LEN { | |
| assert_eq!(Some(VlanHeader::Single(s.clone())), actual.vlan); | |
| } else { | |
| assert_eq!(None, actual.vlan); | |
| } | |
| } | |
| None => { | |
| assert_eq!(None, actual.vlan); | |
| } | |
| } | |
| } | |
| fn compare_ip_header_only(test: &TestPacket, actual: &LaxPacketHeaders) { | |
| assert_eq!( | |
| test.net.as_ref().map(|s| -> NetHeaders { | |
| match s { | |
| NetHeaders::Ipv4(h, _) => NetHeaders::Ipv4(h.clone(), Default::default()), | |
| NetHeaders::Ipv6(h, _) => NetHeaders::Ipv6(h.clone(), Default::default()), | |
| } | |
| }), | |
| actual.net.as_ref().map(|s| -> NetHeaders { | |
| match s { | |
| NetHeaders::Ipv4(h, _) => NetHeaders::Ipv4(h.clone(), Default::default()), | |
| NetHeaders::Ipv6(h, _) => NetHeaders::Ipv6(h.clone(), Default::default()), | |
| } | |
| }) | |
| ); | |
| } | |
| fn compare_transport( | |
| test: &TestPacket, | |
| is_fragmented: bool, | |
| expected_payload: &[u8], | |
| actual: &LaxPacketHeaders, | |
| ) { | |
| if is_fragmented { | |
| assert_eq!(actual.transport, None); | |
| } else { | |
| use TransportHeader as H; | |
| match &actual.transport { | |
| Some(H::Icmpv4(icmpv4)) => { | |
| assert_eq!(&test.transport, &Some(H::Icmpv4(icmpv4.clone()))); | |
| assert_eq!( | |
| actual.payload, | |
| LaxPayloadSlice::Icmpv4 { | |
| payload: expected_payload, | |
| incomplete: false | |
| } | |
| ); | |
| } | |
| Some(H::Icmpv6(icmpv6)) => { | |
| assert_eq!(&test.transport, &Some(H::Icmpv6(icmpv6.clone()))); | |
| assert_eq!( | |
| actual.payload, | |
| LaxPayloadSlice::Icmpv6 { | |
| payload: expected_payload, | |
| incomplete: false | |
| } | |
| ); | |
| } | |
| Some(H::Udp(s)) => { | |
| assert_eq!(&test.transport, &Some(H::Udp(s.clone()))); | |
| assert_eq!( | |
| actual.payload, | |
| LaxPayloadSlice::Udp { | |
| payload: expected_payload, | |
| incomplete: false | |
| } | |
| ); | |
| } | |
| Some(H::Tcp(s)) => { | |
| assert_eq!(&test.transport, &Some(H::Tcp(s.clone()))); | |
| assert_eq!( | |
| actual.payload, | |
| LaxPayloadSlice::Tcp { | |
| payload: expected_payload, | |
| incomplete: false | |
| } | |
| ); | |
| } | |
| None => { | |
| assert_eq!(&test.transport, &None); | |
| } | |
| } | |
| } | |
| } | |
| // from_ethernet_slice | |
| if test.link.is_some() { | |
| if data.len() < Ethernet2Header::LEN { | |
| assert_eq!( | |
| LenError { | |
| required_len: Ethernet2Header::LEN, | |
| len: data.len(), | |
| len_source: LenSource::Slice, | |
| layer: Layer::Ethernet2Header, | |
| layer_start_offset: 0 | |
| }, | |
| LaxPacketHeaders::from_ethernet(&data).unwrap_err() | |
| ); | |
| } else { | |
| let actual = LaxPacketHeaders::from_ethernet(&data).unwrap(); | |
| assert_eq!(actual.stop_err, expected_stop_err); | |
| match expected_stop_err.as_ref().map(|v| v.1) { | |
| None => { | |
| assert_eq!(test.link, actual.link); | |
| compare_vlan(test, data, &actual); | |
| assert_eq!(test.net, actual.net); | |
| compare_transport( | |
| test, | |
| test.is_ip_payload_fragmented(), | |
| expected_payload, | |
| &actual, | |
| ); | |
| } | |
| Some(Layer::VlanHeader) => { | |
| assert_eq!(test.link, actual.link); | |
| compare_vlan(test, data, &actual); | |
| assert_eq!(None, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); | |
| } | |
| Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => { | |
| assert_eq!(test.link, actual.link); | |
| compare_vlan(test, data, &actual); | |
| assert_eq!(None, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); | |
| } | |
| Some(Layer::IpAuthHeader) | |
| | Some(Layer::Ipv6ExtHeader) | |
| | Some(Layer::Ipv6HopByHopHeader) | |
| | Some(Layer::Ipv6DestOptionsHeader) | |
| | Some(Layer::Ipv6RouteHeader) | |
| | Some(Layer::Ipv6FragHeader) => { | |
| assert_eq!(test.link, actual.link); | |
| compare_vlan(test, data, &actual); | |
| compare_ip_header_only(test, &actual); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| Some(Layer::TcpHeader) | |
| | Some(Layer::UdpHeader) | |
| | Some(Layer::Icmpv4) | |
| | Some(Layer::Icmpv6) => { | |
| assert_eq!(test.link, actual.link); | |
| compare_vlan(test, data, &actual); | |
| assert_eq!(test.net, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| _ => unreachable!("error in an unexpected layer"), | |
| } | |
| } | |
| } | |
| // from_ether_type (vlan at start) | |
| if test.link.is_none() && test.vlan.is_some() { | |
| for ether_type in VLAN_ETHER_TYPES { | |
| let actual = LaxPacketHeaders::from_ether_type(ether_type, data); | |
| assert_eq!(actual.stop_err, expected_stop_err); | |
| compare_vlan(test, data, &actual); | |
| match expected_stop_err.as_ref().map(|v| v.1) { | |
| None => { | |
| assert_eq!(test.net, actual.net); | |
| compare_transport( | |
| test, | |
| test.is_ip_payload_fragmented(), | |
| expected_payload, | |
| &actual, | |
| ); | |
| } | |
| Some(Layer::VlanHeader) => { | |
| assert_eq!(None, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); | |
| } | |
| Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => { | |
| assert_eq!(None, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); | |
| } | |
| Some(Layer::IpAuthHeader) | |
| | Some(Layer::Ipv6ExtHeader) | |
| | Some(Layer::Ipv6HopByHopHeader) | |
| | Some(Layer::Ipv6DestOptionsHeader) | |
| | Some(Layer::Ipv6RouteHeader) | |
| | Some(Layer::Ipv6FragHeader) => { | |
| compare_ip_header_only(test, &actual); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| Some(Layer::TcpHeader) | |
| | Some(Layer::UdpHeader) | |
| | Some(Layer::Icmpv4) | |
| | Some(Layer::Icmpv6) => { | |
| assert_eq!(test.net, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| _ => unreachable!("error in an unexpected layer"), | |
| } | |
| } | |
| } | |
| // from_ether_type (ip at start) | |
| if test.link.is_none() && test.vlan.is_none() { | |
| if let Some(ip) = &test.net { | |
| let ether_type = match ip { | |
| NetHeaders::Ipv4(_, _) => ether_type::IPV4, | |
| NetHeaders::Ipv6(_, _) => ether_type::IPV6, | |
| }; | |
| let actual = LaxPacketHeaders::from_ether_type(ether_type, &data); | |
| assert_eq!(actual.stop_err, expected_stop_err); | |
| assert_eq!(None, actual.link); | |
| assert_eq!(test.vlan, None); | |
| match expected_stop_err.as_ref().map(|v| v.1) { | |
| None => { | |
| assert_eq!(test.net, actual.net); | |
| compare_transport( | |
| test, | |
| test.is_ip_payload_fragmented(), | |
| expected_payload, | |
| &actual, | |
| ); | |
| } | |
| Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => { | |
| assert_eq!(None, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert_eq!( | |
| LaxPayloadSlice::Ether(EtherPayloadSlice { | |
| ether_type, | |
| payload: data | |
| }), | |
| actual.payload | |
| ); | |
| } | |
| Some(Layer::IpAuthHeader) | |
| | Some(Layer::Ipv6ExtHeader) | |
| | Some(Layer::Ipv6HopByHopHeader) | |
| | Some(Layer::Ipv6DestOptionsHeader) | |
| | Some(Layer::Ipv6RouteHeader) | |
| | Some(Layer::Ipv6FragHeader) => { | |
| compare_ip_header_only(test, &actual); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| Some(Layer::TcpHeader) | |
| | Some(Layer::UdpHeader) | |
| | Some(Layer::Icmpv4) | |
| | Some(Layer::Icmpv6) => { | |
| assert_eq!(test.net, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| _ => unreachable!("error in an unexpected layer"), | |
| } | |
| } | |
| } | |
| // from_ip_slice | |
| if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { | |
| if let Some(err) = expected_ip_err { | |
| assert_eq!(err, LaxPacketHeaders::from_ip(&data).unwrap_err()); | |
| } else { | |
| let actual = LaxPacketHeaders::from_ip(&data).unwrap(); | |
| assert_eq!(actual.stop_err, expected_stop_err); | |
| assert_eq!(actual.link, None); | |
| assert_eq!(test.vlan, None); | |
| match expected_stop_err.as_ref().map(|v| v.1) { | |
| None => { | |
| assert_eq!(test.net, actual.net); | |
| compare_transport( | |
| test, | |
| test.is_ip_payload_fragmented(), | |
| expected_payload, | |
| &actual, | |
| ); | |
| } | |
| Some(Layer::IpAuthHeader) | |
| | Some(Layer::Ipv6ExtHeader) | |
| | Some(Layer::Ipv6HopByHopHeader) | |
| | Some(Layer::Ipv6DestOptionsHeader) | |
| | Some(Layer::Ipv6RouteHeader) | |
| | Some(Layer::Ipv6FragHeader) => { | |
| compare_ip_header_only(test, &actual); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| Some(Layer::TcpHeader) | |
| | Some(Layer::UdpHeader) | |
| | Some(Layer::Icmpv4) | |
| | Some(Layer::Icmpv6) => { | |
| assert_eq!(test.net, actual.net); | |
| assert_eq!(None, actual.transport); | |
| assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); | |
| } | |
| _ => unreachable!("error in an unexpected layer"), | |
| } | |
| } | |
| } | |
| } | |
| } |