| use crate::*; | |
| /// Packet slice split into multiple slices containing the different headers & payload. | |
| /// | |
| /// Everything that could not be parsed is stored in a slice in the field "payload". | |
| /// | |
| /// You can use | |
| /// | |
| /// * [`SlicedPacket::from_ethernet`] | |
| /// * [`SlicedPacket::from_ether_type`] | |
| /// * [`SlicedPacket::from_ip`] | |
| /// | |
| /// depending on your starting header to slice a packet. | |
| /// | |
| /// # Examples | |
| /// | |
| /// Basic usage: | |
| /// | |
| ///``` | |
| /// # use etherparse::{SlicedPacket, 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 packet = Vec::<u8>::with_capacity( | |
| /// # builder.size(payload.len())); | |
| /// # builder.write(&mut packet, &payload).unwrap(); | |
| /// match SlicedPacket::from_ethernet(&packet) { | |
| /// Err(value) => println!("Err {:?}", value), | |
| /// Ok(value) => { | |
| /// println!("link: {:?}", value.link); | |
| /// println!("vlan: {:?}", value.vlan); | |
| /// println!("net: {:?}", value.net); | |
| /// println!("transport: {:?}", value.transport); | |
| /// } | |
| /// } | |
| /// ``` | |
| #[derive(Clone, Debug, Eq, PartialEq)] | |
| pub struct SlicedPacket<'a> { | |
| /// Ethernet II header if present. | |
| pub link: Option<LinkSlice<'a>>, | |
| /// Single or double vlan headers if present. | |
| pub vlan: Option<VlanSlice<'a>>, | |
| /// IPv4 or IPv6 header, IP extension headers & payload if present. | |
| pub net: Option<NetSlice<'a>>, | |
| /// TCP or UDP header & payload if present. | |
| pub transport: Option<TransportSlice<'a>>, | |
| } | |
| impl<'a> SlicedPacket<'a> { | |
| /// Separates a network packet slice into different slices containing the headers from the ethernet header downwards. | |
| /// | |
| /// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts | |
| /// with an ethernet II header. | |
| /// | |
| /// # Examples | |
| /// | |
| /// Basic usage: | |
| /// | |
| ///``` | |
| /// # use etherparse::{SlicedPacket, 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 packet = Vec::<u8>::with_capacity( | |
| /// # builder.size(payload.len())); | |
| /// # builder.write(&mut packet, &payload).unwrap(); | |
| /// match SlicedPacket::from_ethernet(&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(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> { | |
| SlicedPacketCursor::new(data).slice_ethernet2() | |
| } | |
| /// Separates a network packet slice into different slices containing the | |
| /// headers from the Linux Cooked Capture v1 (SLL) header downwards. | |
| /// | |
| /// The result is returned as a [`SlicedPacket`] struct. This function | |
| /// assumes the given data starts with a Linux Cooked Capture v1 (SLL) | |
| /// header. | |
| /// | |
| /// # Examples | |
| /// | |
| /// Basic usage: | |
| /// | |
| ///``` | |
| /// # use etherparse::{SlicedPacket, PacketBuilder, LinuxSllPacketType}; | |
| /// # let builder = PacketBuilder:: | |
| /// # linux_sll(LinuxSllPacketType::OTHERHOST, //packet type | |
| /// # 6, //sender address valid length | |
| /// # [1,2,3,4,5,6,0,0]) //sender address with padding | |
| /// # .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 packet = Vec::<u8>::with_capacity( | |
| /// # builder.size(payload.len())); | |
| /// # builder.write(&mut packet, &payload).unwrap(); | |
| /// match SlicedPacket::from_linux_sll(&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_linux_sll(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> { | |
| SlicedPacketCursor::new(data).slice_linux_sll() | |
| } | |
| /// Separates a network packet slice into different slices containing the headers using | |
| /// the given `ether_type` number to identify the first header. | |
| /// | |
| /// The result is returned as a [`SlicedPacket`] 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, SlicedPacket}; | |
| /// | |
| /// match SlicedPacket::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( | |
| ether_type: EtherType, | |
| data: &'a [u8], | |
| ) -> Result<SlicedPacket<'a>, err::packet::SliceError> { | |
| use ether_type::*; | |
| let mut cursor = SlicedPacketCursor::new(data); | |
| cursor.result.link = Some(LinkSlice::EtherPayload(EtherPayloadSlice { | |
| ether_type, | |
| payload: data, | |
| })); | |
| match ether_type { | |
| IPV4 => cursor.slice_ipv4(), | |
| IPV6 => cursor.slice_ipv6(), | |
| VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => cursor.slice_vlan(), | |
| _ => Ok(cursor.result), | |
| } | |
| } | |
| /// Separates a network packet slice into different slices containing the headers from the ip header downwards. | |
| /// | |
| /// 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::{SlicedPacket, PacketBuilder}; | |
| /// # 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 | |
| /// # //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(); | |
| /// match SlicedPacket::from_ip(&packet) { | |
| /// Err(value) => println!("Err {:?}", value), | |
| /// Ok(value) => { | |
| /// //link & vlan fields are empty when parsing from ip downwards | |
| /// assert_eq!(None, value.link); | |
| /// assert_eq!(None, value.vlan); | |
| /// | |
| /// //ip & transport (udp or tcp) | |
| /// println!("net: {:?}", value.net); | |
| /// println!("transport: {:?}", value.transport); | |
| /// } | |
| /// } | |
| /// ``` | |
| pub fn from_ip(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> { | |
| SlicedPacketCursor::new(data).slice_ip() | |
| } | |
| /// If the slice in the `payload` field contains an ethernet payload | |
| /// this method returns the ether type number describing the payload type. | |
| /// | |
| /// The ether type number can come from an ethernet II header or a | |
| /// VLAN header depending on which headers are present. | |
| /// | |
| /// In case that `ip` and/or `transport` fields are the filled None | |
| /// is returned, as the payload contents then are defined by a | |
| /// lower layer protocol described in these fields. | |
| pub fn payload_ether_type(&self) -> Option<EtherType> { | |
| if self.net.is_some() || self.transport.is_some() { | |
| None | |
| } else if let Some(vlan) = &self.vlan { | |
| use VlanSlice::*; | |
| match vlan { | |
| SingleVlan(s) => Some(s.ether_type()), | |
| DoubleVlan(d) => Some(d.inner().ether_type()), | |
| } | |
| } else if let Some(link) = &self.link { | |
| use LinkSlice::*; | |
| match link { | |
| Ethernet2(eth) => Some(eth.ether_type()), | |
| LinkSlice::LinuxSll(e) => match e.protocol_type() { | |
| LinuxSllProtocolType::EtherType(EtherType(v)) | |
| | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType( | |
| v, | |
| )) => Some(EtherType(v)), | |
| _ => None, | |
| }, | |
| EtherPayload(e) => Some(e.ether_type), | |
| LinkSlice::LinuxSllPayload(e) => match e.protocol_type { | |
| LinuxSllProtocolType::EtherType(EtherType(v)) | |
| | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType( | |
| v, | |
| )) => Some(EtherType(v)), | |
| _ => None, | |
| }, | |
| } | |
| } else { | |
| None | |
| } | |
| } | |
| /// Returns the last ether payload of the packet (if one is present). | |
| /// | |
| /// If VLAN header is present the payload after the most inner VLAN | |
| /// header is returned and if there is no VLAN header is present in the | |
| /// link field is returned. | |
| pub fn ether_payload(&self) -> Option<EtherPayloadSlice<'a>> { | |
| if let Some(vlan) = self.vlan.as_ref() { | |
| match vlan { | |
| VlanSlice::SingleVlan(s) => Some(s.payload()), | |
| VlanSlice::DoubleVlan(s) => Some(s.payload()), | |
| } | |
| } else if let Some(link) = self.link.as_ref() { | |
| match link { | |
| LinkSlice::Ethernet2(e) => Some(e.payload()), | |
| LinkSlice::LinuxSll(e) => match e.protocol_type() { | |
| LinuxSllProtocolType::EtherType(_) | |
| | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => { | |
| Some(EtherPayloadSlice::try_from(e.payload()).ok()?) | |
| } | |
| _ => None, | |
| }, | |
| LinkSlice::EtherPayload(e) => Some(e.clone()), | |
| LinkSlice::LinuxSllPayload(e) => match e.protocol_type { | |
| LinuxSllProtocolType::EtherType(_) | |
| | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => { | |
| Some(EtherPayloadSlice::try_from(e.clone()).ok()?) | |
| } | |
| _ => None, | |
| }, | |
| } | |
| } else { | |
| None | |
| } | |
| } | |
| /// Return the IP payload after the the IP header and the IP extension | |
| /// headers (if one is present). | |
| pub fn ip_payload(&self) -> Option<&IpPayloadSlice<'a>> { | |
| if let Some(net) = self.net.as_ref() { | |
| use NetSlice::*; | |
| match net { | |
| Ipv4(v) => Some(v.payload()), | |
| Ipv6(v) => Some(v.payload()), | |
| } | |
| } else { | |
| None | |
| } | |
| } | |
| /// Returns true if `net` contains an fragmented IPv4 or IPv6 payload. | |
| pub fn is_ip_payload_fragmented(&self) -> bool { | |
| use NetSlice::*; | |
| match &self.net { | |
| Some(Ipv4(v)) => v.is_payload_fragmented(), | |
| Some(Ipv6(v)) => v.is_payload_fragmented(), | |
| None => false, | |
| } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod test { | |
| use super::*; | |
| use crate::err::{packet::SliceError, Layer, LenError}; | |
| use crate::test_gens::*; | |
| use crate::test_packet::TestPacket; | |
| use proptest::prelude::*; | |
| 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 = SlicedPacket { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }; | |
| assert_eq!(header.clone(), header); | |
| } | |
| #[test] | |
| fn debug() { | |
| use alloc::format; | |
| let header = SlicedPacket { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }; | |
| assert_eq!( | |
| format!("{:?}", header), | |
| format!( | |
| "SlicedPacket {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?} }}", | |
| header.link, header.vlan, header.net, header.transport, | |
| ) | |
| ); | |
| } | |
| #[test] | |
| fn ether_payload() { | |
| use alloc::vec::*; | |
| // no content | |
| assert_eq!( | |
| SlicedPacket { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| } | |
| .ether_payload(), | |
| None | |
| ); | |
| // only ethernet header II | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let mut buf = Vec::with_capacity(Ethernet2Header::LEN + 4); | |
| buf.extend_from_slice( | |
| &Ethernet2Header { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice(&payload); | |
| assert_eq!( | |
| SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(), | |
| Some(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| }) | |
| ); | |
| } | |
| // ether type payload | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| assert_eq!( | |
| SlicedPacket { | |
| link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| })), | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| } | |
| .ether_payload(), | |
| Some(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| }) | |
| ); | |
| } | |
| // only linux_sll payload | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let mut buf = Vec::with_capacity(LinuxSllHeader::LEN + 4); | |
| buf.extend_from_slice( | |
| &LinuxSllHeader { | |
| packet_type: LinuxSllPacketType::HOST, | |
| arp_hrd_type: ArpHardwareId::ETHER, | |
| sender_address_valid_length: 6, | |
| sender_address: [1, 2, 3, 4, 5, 6, 0, 0], | |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN), | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice(&payload); | |
| assert_eq!( | |
| SlicedPacket::from_linux_sll(&buf).unwrap().ether_payload(), | |
| Some(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| }) | |
| ); | |
| } | |
| // ether type payload | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| assert_eq!( | |
| SlicedPacket { | |
| link: Some(LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice { | |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN), | |
| payload: &payload | |
| })), | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| } | |
| .ether_payload(), | |
| Some(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| }) | |
| ); | |
| } | |
| // single vlan header | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN + 4); | |
| buf.extend_from_slice( | |
| &Ethernet2Header { | |
| ether_type: EtherType::VLAN_TAGGED_FRAME, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice( | |
| &SingleVlanHeader { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice(&payload); | |
| assert_eq!( | |
| SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(), | |
| Some(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| }) | |
| ); | |
| } | |
| // double vlan header | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN * 2 + 4); | |
| buf.extend_from_slice( | |
| &Ethernet2Header { | |
| ether_type: EtherType::VLAN_DOUBLE_TAGGED_FRAME, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice( | |
| &SingleVlanHeader { | |
| ether_type: EtherType::VLAN_TAGGED_FRAME, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice( | |
| &SingleVlanHeader { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice(&payload); | |
| assert_eq!( | |
| SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(), | |
| Some(EtherPayloadSlice { | |
| ether_type: EtherType::WAKE_ON_LAN, | |
| payload: &payload | |
| }) | |
| ); | |
| } | |
| } | |
| #[test] | |
| fn ip_payload() { | |
| use alloc::vec::*; | |
| // no content | |
| assert_eq!( | |
| SlicedPacket { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| } | |
| .ip_payload(), | |
| None | |
| ); | |
| // ipv4 | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let mut buf = Vec::with_capacity(Ipv4Header::MIN_LEN + 4); | |
| buf.extend_from_slice( | |
| &Ipv4Header { | |
| protocol: IpNumber::ARIS, | |
| total_len: Ipv4Header::MIN_LEN_U16 + 4, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice(&payload); | |
| assert_eq!( | |
| SlicedPacket::from_ip(&buf).unwrap().ip_payload(), | |
| Some(&IpPayloadSlice { | |
| payload: &payload, | |
| ip_number: IpNumber::ARIS, | |
| fragmented: false, | |
| len_source: LenSource::Ipv4HeaderTotalLen, | |
| }) | |
| ); | |
| } | |
| // ipv6 | |
| { | |
| let payload = [1, 2, 3, 4]; | |
| let mut buf = Vec::with_capacity(Ipv6Header::LEN + 4); | |
| buf.extend_from_slice( | |
| &Ipv6Header { | |
| payload_length: 4, | |
| next_header: IpNumber::ARGUS, | |
| ..Default::default() | |
| } | |
| .to_bytes(), | |
| ); | |
| buf.extend_from_slice(&payload); | |
| assert_eq!( | |
| SlicedPacket::from_ip(&buf).unwrap().ip_payload(), | |
| Some(&IpPayloadSlice { | |
| payload: &payload, | |
| ip_number: IpNumber::ARGUS, | |
| fragmented: false, | |
| len_source: LenSource::Ipv6HeaderPayloadLen, | |
| }) | |
| ); | |
| } | |
| } | |
| #[test] | |
| fn from_x_slice() { | |
| // no eth | |
| from_x_slice_vlan_variants(&TestPacket { | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }); | |
| // eth payload | |
| { | |
| let data = [1, 2, 3, 4]; | |
| let result = SlicedPacket::from_ether_type(EtherType(0x8221), &data).unwrap(); | |
| assert_eq!( | |
| result, | |
| SlicedPacket { | |
| link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { | |
| ether_type: EtherType(0x8221), | |
| payload: &data | |
| })), | |
| 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: Layer::Ethernet2Header, | |
| layer_start_offset: 0, | |
| }; | |
| from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone())); | |
| } | |
| } | |
| } | |
| // linux_sll | |
| { | |
| let linux_sll = LinuxSllHeader { | |
| packet_type: LinuxSllPacketType::HOST, | |
| arp_hrd_type: ArpHardwareId::ETHER, | |
| sender_address_valid_length: 6, | |
| sender_address: [1, 2, 3, 4, 5, 6, 0, 0], | |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN), | |
| }; | |
| let test = TestPacket { | |
| link: Some(LinkHeader::LinuxSll(linux_sll.clone())), | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }; | |
| // eth len error | |
| { | |
| let data = test.to_vec(&[]); | |
| for len in 0..data.len() { | |
| let err = LenError { | |
| required_len: linux_sll.header_len(), | |
| len, | |
| len_source: LenSource::Slice, | |
| layer: Layer::LinuxSllHeader, | |
| 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: 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: 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())); | |
| 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: ipv4.header_len(), | |
| len, | |
| len_source: LenSource::Slice, | |
| layer: 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 = Layer::IpHeader; | |
| err | |
| } else { | |
| err.clone() | |
| } | |
| }) | |
| }, | |
| ); | |
| } | |
| } | |
| // 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]; | |
| 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(Ipv4HeaderLengthSmallerThanHeader { ihl: 0 }) | |
| }, | |
| ); | |
| } | |
| // ipv4 content error (total length too small) | |
| { | |
| 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 err = LenError { | |
| required_len: ipv4.header_len(), | |
| len: 0, | |
| len_source: LenSource::Ipv4HeaderTotalLen, | |
| layer: Layer::Ipv4Packet, | |
| layer_start_offset: { | |
| test.link.as_ref().map(|h| h.header_len()).unwrap_or(0) | |
| + test.vlan.as_ref().map(|h| h.header_len()).unwrap_or(0) | |
| }, | |
| }; | |
| from_slice_assert_err(&test, &data, SliceError::Len(err.clone())); | |
| } | |
| } | |
| // 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, | |
| }; | |
| 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: 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 = 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: 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: 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: 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())); | |
| 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, | |
| }; | |
| 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())); | |
| 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, | |
| }; | |
| from_slice_assert_err( | |
| &test, | |
| &data[..base_len + len], | |
| SliceError::Len(err.clone()), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| fn from_x_slice_assert_ok(test_base: &TestPacket) { | |
| fn assert_test_result(test: &TestPacket, expected_payload: &[u8], result: &SlicedPacket) { | |
| // check if fragmenting | |
| let is_fragmented = test.is_ip_payload_fragmented(); | |
| // check headers | |
| assert_eq!( | |
| test.link, | |
| match result.link.as_ref() { | |
| Some(s) => match s { | |
| LinkSlice::Ethernet2(e) => Some(LinkHeader::Ethernet2(e.to_header())), | |
| LinkSlice::LinuxSll(e) => Some(LinkHeader::LinuxSll(e.to_header())), | |
| LinkSlice::EtherPayload(_) => None, | |
| LinkSlice::LinuxSllPayload(_) => None, | |
| }, | |
| None => None, | |
| } | |
| ); | |
| assert_eq!(test.vlan, result.vlan.as_ref().map(|e| e.to_header())); | |
| assert_eq!( | |
| test.net, | |
| result.net.as_ref().map(|s: &NetSlice| -> NetHeaders { | |
| match s { | |
| NetSlice::Ipv4(ipv4) => NetHeaders::Ipv4( | |
| ipv4.header().to_header(), | |
| ipv4.extensions().to_header(), | |
| ), | |
| NetSlice::Ipv6(ipv6) => NetHeaders::Ipv6( | |
| ipv6.header().to_header(), | |
| Ipv6Extensions::from_slice( | |
| ipv6.header().next_header(), | |
| ipv6.extensions().slice(), | |
| ) | |
| .unwrap() | |
| .0, | |
| ), | |
| } | |
| }) | |
| ); | |
| // check transport header & payload | |
| if is_fragmented { | |
| assert_eq!(result.transport, None); | |
| } else { | |
| use TransportHeader as H; | |
| use TransportSlice as S; | |
| match &result.transport { | |
| Some(S::Icmpv4(icmpv4)) => { | |
| assert_eq!(&test.transport, &Some(H::Icmpv4(icmpv4.header()))); | |
| assert_eq!(icmpv4.payload(), expected_payload); | |
| } | |
| Some(S::Icmpv6(icmpv6)) => { | |
| assert_eq!(&test.transport, &Some(H::Icmpv6(icmpv6.header()))); | |
| assert_eq!(icmpv6.payload(), expected_payload); | |
| } | |
| Some(S::Udp(s)) => { | |
| assert_eq!(&test.transport, &Some(H::Udp(s.to_header()))); | |
| } | |
| Some(S::Tcp(s)) => { | |
| assert_eq!(&test.transport, &Some(H::Tcp(s.to_header()))); | |
| } | |
| None => { | |
| assert_eq!(&test.transport, &None); | |
| } | |
| } | |
| } | |
| } | |
| // 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); | |
| // from_ethernet | |
| if test.link.is_some() { | |
| let result = SlicedPacket::from_ethernet(&data).unwrap(); | |
| assert_test_result(&test, &payload, &result); | |
| } | |
| // from_ether_type (vlan at start) | |
| if test.link.is_none() && test.vlan.is_some() { | |
| for ether_type in VLAN_ETHER_TYPES { | |
| let result = SlicedPacket::from_ether_type(ether_type, &data).unwrap(); | |
| assert_eq!( | |
| result.link, | |
| Some(LinkSlice::EtherPayload(EtherPayloadSlice { | |
| ether_type, | |
| payload: &data | |
| })) | |
| ); | |
| assert_test_result(&test, &payload, &result); | |
| } | |
| } | |
| // 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 result = SlicedPacket::from_ether_type(ether_type, &data).unwrap(); | |
| assert_eq!( | |
| result.link, | |
| Some(LinkSlice::EtherPayload(EtherPayloadSlice { | |
| ether_type, | |
| payload: &data | |
| })) | |
| ); | |
| assert_test_result(&test, &payload, &result); | |
| } | |
| } | |
| // from_ip_slice | |
| if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { | |
| let result = SlicedPacket::from_ip(&data).unwrap(); | |
| assert_test_result(&test, &payload, &result); | |
| } | |
| } | |
| /// 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 let Some(ref header) = test.link { | |
| match header { | |
| LinkHeader::Ethernet2(_) => { | |
| assert_eq!(err.clone(), SlicedPacket::from_ethernet(&data).unwrap_err()) | |
| } | |
| LinkHeader::LinuxSll(_) => assert_eq!( | |
| err.clone(), | |
| SlicedPacket::from_linux_sll(&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(), | |
| SlicedPacket::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 = SlicedPacket::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, SlicedPacket::from_ip(&data).unwrap_err()); | |
| } | |
| } | |
| proptest! { | |
| #[test] | |
| fn payload_ether_type( | |
| ref eth in ethernet_2_unknown(), | |
| ref linux_sll in linux_sll_any(), | |
| ref vlan_outer in vlan_single_unknown(), | |
| ref vlan_inner in vlan_single_unknown(), | |
| ref ipv4 in ipv4_unknown(), | |
| ref udp in udp_any(), | |
| ) { | |
| use IpHeaders::*; | |
| use alloc::vec::Vec; | |
| // empty | |
| { | |
| let s = SlicedPacket{ | |
| link: None, | |
| vlan: None, | |
| net: None, | |
| transport: None, | |
| }; | |
| assert_eq!(None, s.payload_ether_type()); | |
| } | |
| // only linux sll | |
| { | |
| let mut serialized = Vec::with_capacity(linux_sll.header_len()); | |
| eth.write(&mut serialized).unwrap(); | |
| let ether_type = match linux_sll.protocol_type { | |
| LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => Some(EtherType(v)), | |
| _ => None, | |
| }; | |
| if let Ok(s) = SlicedPacket::from_linux_sll(&serialized) { | |
| assert_eq!( | |
| ether_type, | |
| s.payload_ether_type() | |
| ); | |
| } | |
| } | |
| // only ethernet | |
| { | |
| let mut serialized = Vec::with_capacity(eth.header_len()); | |
| eth.write(&mut serialized).unwrap(); | |
| assert_eq!( | |
| Some(eth.ether_type), | |
| SlicedPacket::from_ethernet(&serialized) | |
| .unwrap() | |
| .payload_ether_type() | |
| ); | |
| } | |
| // with single vlan | |
| { | |
| let mut eth_mod = eth.clone(); | |
| eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME; | |
| let mut serialized = Vec::with_capacity( | |
| eth_mod.header_len() + | |
| vlan_outer.header_len() | |
| ); | |
| eth_mod.write(&mut serialized).unwrap(); | |
| vlan_outer.write(&mut serialized).unwrap(); | |
| assert_eq!( | |
| Some(vlan_outer.ether_type), | |
| SlicedPacket::from_ethernet(&serialized) | |
| .unwrap() | |
| .payload_ether_type() | |
| ); | |
| } | |
| // with double vlan | |
| { | |
| let mut eth_mod = eth.clone(); | |
| eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME; | |
| let mut vlan_outer_mod = vlan_outer.clone(); | |
| vlan_outer_mod.ether_type = ether_type::VLAN_TAGGED_FRAME; | |
| let mut serialized = Vec::with_capacity( | |
| eth_mod.header_len() + | |
| vlan_outer_mod.header_len() + | |
| vlan_inner.header_len() | |
| ); | |
| eth_mod.write(&mut serialized).unwrap(); | |
| vlan_outer_mod.write(&mut serialized).unwrap(); | |
| vlan_inner.write(&mut serialized).unwrap(); | |
| assert_eq!( | |
| Some(vlan_inner.ether_type), | |
| SlicedPacket::from_ethernet(&serialized) | |
| .unwrap() | |
| .payload_ether_type() | |
| ); | |
| } | |
| // with ip | |
| { | |
| let builder = PacketBuilder::ethernet2(eth.source, eth.destination) | |
| .ip(Ipv4(ipv4.clone(), Default::default())); | |
| let mut serialized = Vec::with_capacity(builder.size(0)); | |
| builder.write(&mut serialized, ipv4.protocol, &[]).unwrap(); | |
| assert_eq!( | |
| None, | |
| SlicedPacket::from_ethernet(&serialized) | |
| .unwrap() | |
| .payload_ether_type() | |
| ); | |
| } | |
| // with transport | |
| { | |
| let builder = PacketBuilder::ethernet2(eth.source, eth.destination) | |
| .ip(Ipv4(ipv4.clone(), Default::default())) | |
| .udp(udp.source_port, udp.destination_port); | |
| let mut serialized = Vec::with_capacity(builder.size(0)); | |
| builder.write(&mut serialized, &[]).unwrap(); | |
| assert_eq!( | |
| None, | |
| SlicedPacket::from_ethernet(&serialized) | |
| .unwrap() | |
| .payload_ether_type() | |
| ); | |
| } | |
| } | |
| } | |
| } |