| use crate::err::LenError; |
| |
| use super::*; |
| |
| /// Decoded packet headers (data link layer and lower). |
| /// |
| /// You can use |
| /// |
| /// * [`PacketHeaders::from_ethernet_slice`] |
| /// * [`PacketHeaders::from_ether_type`] |
| /// * [`PacketHeaders::from_ip_slice`] |
| /// |
| /// 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 PacketHeaders<'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: PayloadSlice<'a>, |
| } |
| |
| impl<'a> PacketHeaders<'a> { |
| /// Decodes a network packet into different headers from a slice that starts with an Ethernet II header. |
| /// |
| /// The result is returned as a [`PacketHeaders`] struct. |
| /// |
| /// # 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]) //destination mac |
| /// # .ipv4([192,168,1,1], //source ip |
| /// # [192,168,1,2], //destination ip |
| /// # 20) //time to life |
| /// # .udp(21, //source port |
| /// # 1234); // destination 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, PacketHeaders}; |
| /// |
| /// match PacketHeaders::from_ether_type(ether_type::IPV4, packet) { |
| /// Err(value) => println!("Err {:?}", value), |
| /// Ok(value) => { |
| /// println!("link: {:?}", value.link); |
| /// println!("vlan: {:?}", value.vlan); |
| /// println!("net: {:?}", value.net); |
| /// println!("transport: {:?}", value.transport); |
| /// } |
| /// } |
| /// ``` |
| pub fn from_ethernet_slice( |
| slice: &'a [u8], |
| ) -> Result<PacketHeaders<'a>, err::packet::SliceError> { |
| use err::packet::SliceError::Len; |
| |
| let (ethernet, rest) = Ethernet2Header::from_slice(slice).map_err(Len)?; |
| let mut result = Self::from_ether_type(ethernet.ether_type, rest); |
| |
| match &mut result { |
| // inject ethernet header into the result |
| Ok(result) => result.link = Some(LinkHeader::Ethernet2(ethernet)), |
| // add the ethernet header to the overall offset in case there is a length error |
| Err(Len(err)) => err.layer_start_offset += Ethernet2Header::LEN, |
| _ => {} |
| } |
| result |
| } |
| |
| /// Tries to decode a network packet into different headers using the |
| /// given `ether_type` number to identify the first header. |
| /// |
| /// The result is returned as a [`PacketHeaders`] 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]) //destination mac |
| /// # .ipv4([192,168,1,1], //source ip |
| /// # [192,168,1,2], //destination ip |
| /// # 20) //time to life |
| /// # .udp(21, //source port |
| /// # 1234); // destination 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, PacketHeaders}; |
| /// |
| /// match PacketHeaders::from_ether_type(ether_type::IPV4, packet) { |
| /// Err(value) => println!("Err {:?}", value), |
| /// Ok(value) => { |
| /// println!("link: {:?}", value.link); |
| /// println!("vlan: {:?}", value.vlan); |
| /// println!("net: {:?}", value.net); |
| /// println!("transport: {:?}", value.transport); |
| /// } |
| /// } |
| /// ``` |
| pub fn from_ether_type( |
| mut ether_type: EtherType, |
| slice: &'a [u8], |
| ) -> Result<PacketHeaders<'a>, err::packet::SliceError> { |
| use err::packet::SliceError::*; |
| |
| let mut rest = slice; |
| |
| // helper function to add the current offset to length errors |
| let add_offset = |mut len_error: LenError, rest: &[u8]| -> LenError { |
| len_error.layer_start_offset += unsafe { |
| // SAFETY: Safe as rest is a subslice of slice. |
| rest.as_ptr().offset_from(slice.as_ptr()) as usize |
| }; |
| len_error |
| }; |
| |
| let mut result = PacketHeaders { |
| link: None, |
| vlan: None, |
| net: None, |
| transport: None, |
| payload: PayloadSlice::Ether(EtherPayloadSlice { |
| ether_type, |
| payload: rest, |
| }), |
| }; |
| |
| //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) = SingleVlanHeader::from_slice(rest).map_err(Len)?; |
| |
| //set the rest & ether_type for the following operations |
| rest = outer_rest; |
| ether_type = outer.ether_type; |
| result.payload = PayloadSlice::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) = SingleVlanHeader::from_slice(rest) |
| .map_err(|err| Len(err.add_offset(SingleVlanHeader::LEN)))?; |
| |
| //set the rest & ether_type for the following operations |
| rest = inner_rest; |
| ether_type = inner.ether_type; |
| result.payload = PayloadSlice::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 => { |
| // read ipv4 header & extensions and payload slice |
| let (ip, ip_payload) = IpHeaders::from_ipv4_slice(rest).map_err(|err| { |
| use err::ipv4::SliceError as I; |
| match err { |
| I::Len(err) => Len(add_offset(err, rest)), |
| I::Header(err) => Ipv4(err), |
| I::Exts(err) => Ipv4Exts(err), |
| } |
| })?; |
| |
| // set the next |
| rest = ip_payload.payload; |
| result.net = Some(ip.into()); |
| result.payload = PayloadSlice::Ip(ip_payload.clone()); |
| |
| // decode transport layer |
| let (transport, payload) = read_transport(ip_payload).map_err(|err| { |
| use err::tcp::HeaderSliceError as I; |
| match err { |
| I::Len(err) => Len(add_offset(err, rest)), |
| I::Content(err) => Tcp(err), |
| } |
| })?; |
| |
| result.transport = transport; |
| result.payload = payload; |
| } |
| IPV6 => { |
| // read ipv6 header & extensions and payload slice |
| let (ip, ip_payload) = IpHeaders::from_ipv6_slice(rest).map_err(|err| { |
| use err::ipv6::SliceError as I; |
| match err { |
| I::Len(err) => Len(add_offset(err, rest)), |
| I::Header(err) => Ipv6(err), |
| I::Exts(err) => Ipv6Exts(err), |
| } |
| })?; |
| |
| //set the ip result & rest |
| rest = ip_payload.payload; |
| result.net = Some(ip.into()); |
| result.payload = PayloadSlice::Ip(ip_payload.clone()); |
| |
| // decode transport layer |
| let (transport, payload) = read_transport(ip_payload).map_err(|err| { |
| use err::tcp::HeaderSliceError as I; |
| match err { |
| I::Len(err) => Len(add_offset(err, rest)), |
| I::Content(err) => Tcp(err), |
| } |
| })?; |
| |
| result.transport = transport; |
| result.payload = payload; |
| } |
| _ => {} |
| }; |
| |
| Ok(result) |
| } |
| |
| /// Tries to decode an ip packet and its transport headers. |
| /// |
| /// Assumes the given slice starts with the first byte of the IP header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # // build a UDP packet |
| /// # let payload = [0u8;18]; |
| /// # 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); // destination port |
| /// # |
| /// # // serialize the packet |
| /// # let packet = { |
| /// # let mut packet = Vec::<u8>::with_capacity( |
| /// # builder.size(payload.len()) |
| /// # ); |
| /// # builder.write(&mut packet, &payload).unwrap(); |
| /// # packet |
| /// # }; |
| /// use etherparse::PacketHeaders; |
| /// |
| /// match PacketHeaders::from_ip_slice(&packet) { |
| /// Err(value) => println!("Err {:?}", value), |
| /// Ok(value) => { |
| /// println!("link: {:?}", value.link); |
| /// println!("vlan: {:?}", value.vlan); |
| /// println!("net: {:?}", value.net); |
| /// println!("transport: {:?}", value.transport); |
| /// } |
| /// } |
| /// ``` |
| pub fn from_ip_slice(slice: &[u8]) -> Result<PacketHeaders, err::packet::SliceError> { |
| use err::packet::SliceError::*; |
| |
| // read ip headers |
| let (ip_header, ip_payload) = IpHeaders::from_slice(slice).map_err(|err| { |
| use err::ip::HeadersSliceError as I; |
| match err { |
| I::Len(err) => Len(err), |
| I::Content(err) => match err { |
| err::ip::HeadersError::Ip(err) => Ip(err), |
| err::ip::HeadersError::Ipv4Ext(err) => Ipv4Exts(err), |
| err::ip::HeadersError::Ipv6Ext(err) => Ipv6Exts(err), |
| }, |
| } |
| })?; |
| |
| let mut result = PacketHeaders { |
| link: None, |
| vlan: None, |
| net: Some(ip_header.into()), |
| transport: None, |
| payload: PayloadSlice::Ip(ip_payload.clone()), |
| }; |
| |
| // cache rest for offset addition |
| let rest = ip_payload.payload; |
| |
| // try to parse the transport header (only if data is not fragmented) |
| let (transport, payload) = read_transport(ip_payload).map_err(|err| { |
| use err::tcp::HeaderSliceError as I; |
| match err { |
| I::Len(mut err) => { |
| err.layer_start_offset += unsafe { |
| // SAFETY: Safe as rest is a subslice of slice. |
| rest.as_ptr().offset_from(slice.as_ptr()) as usize |
| }; |
| Len(err) |
| } |
| I::Content(err) => Tcp(err), |
| } |
| })?; |
| |
| // update output |
| result.transport = transport; |
| result.payload = payload; |
| |
| Ok(result) |
| } |
| } |
| |
| /// helper function to process transport headers |
| fn read_transport( |
| ip_payload: IpPayloadSlice, |
| ) -> Result<(Option<TransportHeader>, PayloadSlice), err::tcp::HeaderSliceError> { |
| if ip_payload.fragmented { |
| Ok((None, PayloadSlice::Ip(ip_payload))) |
| } else { |
| // helper function to set the len source in len errors |
| let add_len_source = |mut len_error: LenError| -> err::tcp::HeaderSliceError { |
| // 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(len_error) |
| }; |
| use crate::ip_number::*; |
| use err::tcp::HeaderSliceError::*; |
| match ip_payload.ip_number { |
| ICMP => Icmpv4Slice::from_slice(ip_payload.payload) |
| .map_err(add_len_source) |
| .map(|value| { |
| ( |
| Some(TransportHeader::Icmpv4(value.header())), |
| PayloadSlice::Icmpv4(value.payload()), |
| ) |
| }), |
| IPV6_ICMP => Icmpv6Slice::from_slice(ip_payload.payload) |
| .map_err(add_len_source) |
| .map(|value| { |
| ( |
| Some(TransportHeader::Icmpv6(value.header())), |
| PayloadSlice::Icmpv6(value.payload()), |
| ) |
| }), |
| UDP => UdpHeader::from_slice(ip_payload.payload) |
| .map_err(add_len_source) |
| .map(|value| { |
| ( |
| Some(TransportHeader::Udp(value.0)), |
| PayloadSlice::Udp(value.1), |
| ) |
| }), |
| TCP => TcpHeader::from_slice(ip_payload.payload) |
| .map_err(|err| match err { |
| Len(err) => add_len_source(err), |
| Content(err) => Content(err), |
| }) |
| .map(|value| { |
| ( |
| Some(TransportHeader::Tcp(value.0)), |
| PayloadSlice::Tcp(value.1), |
| ) |
| }), |
| _ => Ok((None, PayloadSlice::Ip(ip_payload))), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::err::packet::SliceError; |
| 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 debug() { |
| use alloc::format; |
| let header = PacketHeaders { |
| link: None, |
| vlan: None, |
| net: None, |
| transport: None, |
| payload: PayloadSlice::Ether(EtherPayloadSlice { |
| ether_type: EtherType(0), |
| payload: &[], |
| }), |
| }; |
| assert_eq!( |
| &format!("{:?}", header), |
| &format!( |
| "PacketHeaders {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?}, payload: {:?} }}", |
| header.link, |
| header.vlan, |
| header.net, |
| header.transport, |
| header.payload |
| ) |
| ); |
| } |
| |
| #[test] |
| fn clone_eq() { |
| let header = PacketHeaders { |
| link: None, |
| vlan: None, |
| net: None, |
| transport: None, |
| payload: PayloadSlice::Ether(EtherPayloadSlice { |
| ether_type: EtherType(0), |
| payload: &[], |
| }), |
| }; |
| assert_eq!(header.clone(), header); |
| } |
| |
| #[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() { |
| let err = LenError { |
| required_len: eth.header_len(), |
| len, |
| len_source: LenSource::Slice, |
| layer: err::Layer::Ethernet2Header, |
| layer_start_offset: 0, |
| }; |
| |
| from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone())); |
| } |
| } |
| } |
| } |
| |
| 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: err::Layer::VlanHeader, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| } |
| } |
| } |
| |
| // 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: err::Layer::VlanHeader, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| 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())); |
| |
| // 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: ipv4.header_len(), |
| len, |
| len_source: LenSource::Slice, |
| layer: err::Layer::Ipv4Header, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| if test.link.is_some() || test.vlan.is_some() { |
| SliceError::Len(err.clone()) |
| } else { |
| SliceError::Len({ |
| if len < 1 { |
| let mut err = err.clone(); |
| err.required_len = 1; |
| err.layer = err::Layer::IpHeader; |
| err |
| } else { |
| err.clone() |
| } |
| }) |
| }, |
| ); |
| } |
| } |
| |
| // ipv4 content error |
| { |
| 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]; |
| |
| from_slice_assert_err( |
| &test, |
| &data, |
| if test.link.is_some() || test.vlan.is_some() { |
| SliceError::Ipv4( |
| err::ipv4::HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 }, |
| ) |
| } else { |
| SliceError::Ip( |
| err::ip::HeaderError::Ipv4HeaderLengthSmallerThanHeader { ihl: 0 }, |
| ) |
| }, |
| ); |
| } |
| } |
| |
| // 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: err::Layer::IpAuthHeader, |
| layer_start_offset: base_len, |
| }; |
| |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| |
| // 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 |
| let err = err::ip_auth::HeaderError::ZeroPayloadLen; |
| from_slice_assert_err(&test, &data, SliceError::Ipv4Exts(err.clone())); |
| } |
| } |
| } |
| |
| // 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: ipv6.header_len(), |
| len, |
| len_source: LenSource::Slice, |
| layer: err::Layer::Ipv6Header, |
| layer_start_offset: base_len, |
| }; |
| |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| if test.link.is_some() || test.vlan.is_some() { |
| SliceError::Len(err.clone()) |
| } else { |
| SliceError::Len({ |
| if len < 1 { |
| let mut err = err.clone(); |
| err.required_len = 1; |
| err.layer = err::Layer::IpHeader; |
| err |
| } else { |
| err.clone() |
| } |
| }) |
| }, |
| ); |
| } |
| } |
| |
| // content error ipv6 |
| { |
| use err::ip::HeaderError::*; |
| 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; |
| |
| from_slice_assert_err( |
| &test, |
| &data, |
| if test.link.is_some() || test.vlan.is_some() { |
| SliceError::Ipv6(err::ipv6::HeaderError::UnexpectedVersion { |
| version_number: 0, |
| }) |
| } else { |
| SliceError::Ip(UnsupportedIpVersion { version_number: 0 }) |
| }, |
| ); |
| } |
| } |
| |
| // 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: err::Layer::IpAuthHeader, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| |
| // 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; |
| |
| let err = err::ip_auth::HeaderError::ZeroPayloadLen; |
| from_slice_assert_err( |
| &test, |
| &data, |
| SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth(err.clone())), |
| ); |
| } |
| |
| // 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; |
| |
| from_slice_assert_err( |
| &test, |
| &data, |
| SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart), |
| ); |
| } |
| } |
| } |
| } |
| |
| 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: err::Layer::UdpHeader, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| } |
| } |
| |
| // 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: err::Layer::TcpHeader, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| |
| // 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 }; |
| from_slice_assert_err(&test, &data, SliceError::Tcp(err.clone())); |
| } |
| } |
| } |
| |
| // 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())); |
| |
| // 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: err::Layer::Icmpv4, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| } |
| } |
| |
| // 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())); |
| |
| // 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: err::Layer::Icmpv6, |
| layer_start_offset: base_len, |
| }; |
| from_slice_assert_err( |
| &test, |
| &data[..base_len + len], |
| SliceError::Len(err.clone()), |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| fn from_x_slice_assert_ok(test_base: &TestPacket) { |
| 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 |
| }; |
| |
| // check if fragmenting |
| let is_fragmented = test.is_ip_payload_fragmented(); |
| |
| // write data |
| let data = test.to_vec(&payload); |
| |
| // from_ethernet_slice |
| if test.link.is_some() { |
| let result = PacketHeaders::from_ethernet_slice(&data).unwrap(); |
| assert_eq!(result.link, test.link); |
| assert_eq!(result.vlan, test.vlan); |
| assert_eq!(result.net, test.net); |
| if is_fragmented { |
| assert_eq!(result.transport, None); |
| } else { |
| assert_eq!(result.transport, test.transport); |
| assert_eq!(result.payload.slice(), &[1, 2, 3, 4]); |
| } |
| } |
| // from_ether_type (vlan at start) |
| if test.link.is_none() && test.vlan.is_some() { |
| for ether_type in VLAN_ETHER_TYPES { |
| let result = PacketHeaders::from_ether_type(ether_type, &data).unwrap(); |
| assert_eq!(result.link, test.link); |
| assert_eq!(result.vlan, test.vlan); |
| assert_eq!(result.net, test.net); |
| if is_fragmented { |
| assert_eq!(result.transport, None); |
| } else { |
| assert_eq!(result.transport, test.transport); |
| assert_eq!(result.payload.slice(), &[1, 2, 3, 4]); |
| } |
| } |
| } |
| // from_ether_type (ip at start) |
| if test.link.is_none() && test.vlan.is_none() { |
| if let Some(ip) = &test.net { |
| let result = PacketHeaders::from_ether_type( |
| match ip { |
| NetHeaders::Ipv4(_, _) => ether_type::IPV4, |
| NetHeaders::Ipv6(_, _) => ether_type::IPV6, |
| }, |
| &data, |
| ) |
| .unwrap(); |
| assert_eq!(result.link, test.link); |
| assert_eq!(result.vlan, test.vlan); |
| assert_eq!(result.net, test.net); |
| if is_fragmented { |
| assert_eq!(result.transport, None); |
| } else { |
| assert_eq!(result.transport, test.transport); |
| assert_eq!(result.payload.slice(), &[1, 2, 3, 4]); |
| } |
| } |
| } |
| // from_ip_slice |
| if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { |
| let result = PacketHeaders::from_ip_slice(&data).unwrap(); |
| assert_eq!(result.link, test.link); |
| assert_eq!(result.vlan, test.vlan); |
| assert_eq!(result.net, test.net); |
| if is_fragmented { |
| assert_eq!(result.transport, None); |
| } else { |
| assert_eq!(result.transport, test.transport); |
| assert_eq!(result.payload.slice(), &[1, 2, 3, 4]); |
| } |
| } |
| } |
| |
| /// Check that the given errors get triggered if presented with the given |
| /// data. |
| fn from_slice_assert_err(test: &TestPacket, data: &[u8], err: SliceError) { |
| // from_ethernet_slice |
| if test.link.is_some() { |
| assert_eq!( |
| err.clone(), |
| PacketHeaders::from_ethernet_slice(&data).unwrap_err() |
| ); |
| } |
| // from_ether_type (vlan at start) |
| if test.link.is_none() && test.vlan.is_some() { |
| for ether_type in VLAN_ETHER_TYPES { |
| assert_eq!( |
| err.clone(), |
| PacketHeaders::from_ether_type(ether_type, &data).unwrap_err() |
| ); |
| } |
| } |
| // from_ether_type (ip at start) |
| if test.link.is_none() && test.vlan.is_none() { |
| if let Some(ip) = &test.net { |
| let err = PacketHeaders::from_ether_type( |
| match ip { |
| NetHeaders::Ipv4(_, _) => ether_type::IPV4, |
| NetHeaders::Ipv6(_, _) => ether_type::IPV6, |
| }, |
| &data, |
| ) |
| .unwrap_err(); |
| assert_eq!(err, err.clone()); |
| } |
| } |
| // from_ip_slice |
| if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { |
| assert_eq!(err, PacketHeaders::from_ip_slice(&data).unwrap_err()); |
| } |
| } |
| } |