diff --git a/README.md b/README.md index 7385cf94..311281e7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A zero allocation supporting library for parsing & writing a bunch of packet bas Currently supported are: * Ethernet II * IEEE 802.1Q VLAN Tagging Header +* MACsec (IEEE 802.1AE) * ARP * IPv4 * IPv6 (supporting the most common extension headers, but not all) @@ -49,11 +50,11 @@ match SlicedPacket::from_ethernet(&packet) { Err(value) => println!("Err {:?}", value), Ok(value) => { println!("link: {:?}", value.link); - println!("vlan: {:?}", value.vlan); + println!("link_exts: {:?}", value.link_exts); // contains vlan & macsec println!("net: {:?}", value.net); // contains ip & arp println!("transport: {:?}", value.transport); } -} +}; ``` This is the faster option if your code is not interested in all fields of all the headers. It is a good choice if you just want filter or find packets based on a subset of the headers and/or their fields. @@ -77,11 +78,11 @@ match PacketHeaders::from_ethernet_slice(&packet) { Err(value) => println!("Err {:?}", value), Ok(value) => { println!("link: {:?}", value.link); - println!("vlan: {:?}", value.vlan); + println!("link_exts: {:?}", value.link_exts); // contains vlan & macsec println!("net: {:?}", value.net); // contains ip & arp println!("transport: {:?}", value.transport); } -} +}; ``` This option is slower then slicing when only few fields are accessed. But it can be the faster option or useful if you are interested in most fields anyways or if you want to re-serialize the headers with modified values. @@ -103,7 +104,8 @@ It is also possible to only slice one packet layer: * [`Ethernet2Slice::from_slice_without_fcs`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Slice.html#method.from_slice_without_fcs) & [`Ethernet2Slice::from_slice_with_crc32_fcs`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Slice.html#method.from_slice_with_crc32_fcs) * [`LinuxSllSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllSlice.html#method.from_slice) -* [`SingleVlanSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanSlice.html#method.from_slice) & [`DoubleVlanSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanSlice.html#method.from_slice) +* [`SingleVlanSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanSlice.html#method.from_slice) +* [`MacsecSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.MacsecSlice.html#method.from_slice) * [`ArpPacketSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.ArpPacketSlice.html#method.from_slice) * [`IpSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.IpSlice.html#method.from_slice) & [`LaxIpSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.LaxIpSlice.html#method.from_slice) * [`Ipv4Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Slice.html#method.from_slice) & [`LaxIpv4Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LaxIpv4Slice.html#method.from_slice) @@ -126,7 +128,7 @@ following \[NAME\]HeaderSlice.from_slice methods, if you want to just slice the * [`Ethernet2HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2HeaderSlice.html#method.from_slice) * [`LinuxSllHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeaderSlice.html#method.from_slice) * [`SingleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeaderSlice.html#method.from_slice) -* [`DoubleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeaderSlice.html#method.from_slice) +* [`MacsecHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.MacsecHeaderSlice.html#method.from_slice) * [`Ipv4HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4HeaderSlice.html#method.from_slice) * [`Ipv4ExtensionsSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4ExtensionsSlice.html#method.from_slice) * [`Ipv6HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6HeaderSlice.html#method.from_slice) @@ -142,7 +144,7 @@ And for deserialization into the corresponding header structs have a look at: * [`Ethernet2Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.read) & [`Ethernet2Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.from_slice) * [`LinuxSllHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.read) & [`LinuxSllHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.from_slice) * [`SingleVlanHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.read) & [`SingleVlanHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.from_slice) -* [`DoubleVlanHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.read) & [`DoubleVlanHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.from_slice) +* [`MacsecHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.MacsecHeader.html#method.read) & [`MacsecHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.MacsecHeader.html#method.from_slice) * [`ArpPacket::read`](https://docs.rs/etherparse/~0/etherparse/struct.ArpPacket.html#method.read) & [`ArpPacket::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.ArpPacket.html#method.from_slice) * [`IpHeaders::read`](https://docs.rs/etherparse/~0/etherparse/enum.IpHeaders.html#method.read) & [`IpHeaders::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.IpHeaders.html#method.from_slice) * [`Ipv4Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.read) & [`Ipv4Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.from_slice) @@ -200,7 +202,7 @@ Read the documentations of the different methods for a more details: * [`Ethernet2Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.to_bytes) & [`Ethernet2Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.write) * [`LinuxSllHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.to_bytes) & [`LinuxSllHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.write) * [`SingleVlanHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.to_bytes) & [`SingleVlanHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.write) -* [`DoubleVlanHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.to_bytes) & [`DoubleVlanHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.write) +* [`MacsecHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.MacsecHeader.html#method.to_bytes) & [`MacsecHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.MacsecHeader.html#method.write) * [`ArpPacket::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.ArpPacket.html#method.to_bytes) & [`ArpPacket::write`](https://docs.rs/etherparse/~0/etherparse/struct.ArpPacket.html#method.write) * [`ArpEthIpv4Packet::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.ArpEthIpv4Packet.html#method.to_bytes) * [`Ipv4Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.to_bytes) & [`Ipv4Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.write) & [`Ipv4Header::write_raw`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.write_raw) @@ -245,6 +247,8 @@ Read the documentations of the different methods for a more details: * [Linux packet types definitions](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_packet.h?id=e33c4963bf536900f917fb65a687724d5539bc21) on the Linux kernel * Address Resolution Protocol (ARP) Parameters [Harware Types](https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2) * [Arp hardware identifiers definitions](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_arp.h?id=e33c4963bf536900f917fb65a687724d5539bc21) on the Linux kernel +* ["IEEE Standard for Local and metropolitan area networks-Media Access Control (MAC) Security," in IEEE Std 802.1AE-2018 (Revision of IEEE Std 802.1AE-2006) , vol., no., pp.1-239, 26 Dec. 2018, doi: 10.1109/IEEESTD.2018.8585421.](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8585421&isnumber=8585420) +* ["IEEE Standard for Local and metropolitan area networks--Media Access Control (MAC) Security Corrigendum 1: Tag Control Information Figure," in IEEE Std 802.1AE-2018/Cor 1-2020 (Corrigendum to IEEE Std 802.1AE-2018) , vol., no., pp.1-14, 21 July 2020, doi: 10.1109/IEEESTD.2020.9144679.](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=9144679&isnumber=9144678) ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option. The corresponding license texts can be found in the LICENSE-APACHE file and the LICENSE-MIT file. diff --git a/etherparse/examples/read_by_slicing.rs b/etherparse/examples/read_by_slicing.rs index 2186f945..1aa95be0 100644 --- a/etherparse/examples/read_by_slicing.rs +++ b/etherparse/examples/read_by_slicing.rs @@ -18,7 +18,7 @@ fn main() { ) .udp( 21, //source port - 1234, //desitnation port + 1234, //destination port ); //payload of the udp packet @@ -31,12 +31,12 @@ fn main() { //slice the packet into the different header components let sliced_packet = SlicedPacket::from_ethernet(&serialized); - //print some informations about the sliced packet + //print some information about the sliced packet match sliced_packet { Err(value) => println!("Err {:?}", value), Ok(value) => { println!("Ok"); - use etherparse::{LinkSlice::*, NetSlice::*, TransportSlice::*, VlanSlice::*}; + use etherparse::{LinkExtSlice::*, LinkSlice::*, NetSlice::*, TransportSlice::*}; match value.link { Some(Ethernet2(value)) => println!( @@ -61,14 +61,11 @@ fn main() { None => {} } - match value.vlan { - Some(SingleVlan(value)) => println!(" SingleVlan {:?}", value.vlan_identifier()), - Some(DoubleVlan(value)) => println!( - " DoubleVlan {:?}, {:?}", - value.outer().vlan_identifier(), - value.inner().vlan_identifier() - ), - None => {} + for link_ext in &value.link_exts { + match link_ext { + Vlan(s) => println!(" Vlan {:?}", s.vlan_identifier()), + Macsec(m) => println!(" Macsec {:?}", m.header.ptype()), + } } match value.net { diff --git a/etherparse/proptest-regressions/compositions_tests.txt b/etherparse/proptest-regressions/compositions_tests.txt index edd40dac..5cb5f36c 100644 --- a/etherparse/proptest-regressions/compositions_tests.txt +++ b/etherparse/proptest-regressions/compositions_tests.txt @@ -7,3 +7,4 @@ cc 93464c2fb682bf96a32f9800d3932df8611a278bf6c993dc3ad6301d17795715 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0 }, ref vlan_outer = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref vlan_inner = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref ipv4 = Ipv4Header { ihl: 7, differentiated_services_code_point: 0, explicit_congestion_notification: 0, payload_len: 0, identification: 0, dont_fragment: false, more_fragments: false, fragments_offset: 0, time_to_live: 0, protocol: 4, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0] }, ref ipv4_exts = Ipv4Extensions { auth: None }, ref ipv6 = Ipv6Header { traffic_class: 213, flow_label: 798389, payload_length: 24896, next_header: 187, hop_limit: 229, source: [14, 32, 160, 168, 37, 154, 115, 40, 38, 87, 212, 112, 188, 142, 254, 197], destination: [6, 159, 253, 179, 126, 197, 144, 208, 190, 191, 89, 166, 208, 140, 54, 50] }, ref ipv6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader { next_header: 156, fragment_offset: 2564, more_fragments: false, identification: 3123850911 }), auth: None }, ref udp = UdpHeader { source_port: 45157, destination_port: 34201, length: 57104, checksum: 21037 }, ref tcp = TcpHeader { source_port: 51159, destination_port: 19610, sequence_number: 3703908533, acknowledgment_number: 8047906, data_offset: 13, ns: true, fin: false, syn: false, rst: false, psh: false, ack: false, urg: true, ece: false, cwr: true, window_size: 3326, checksum: 50866, urgent_pointer: 1068, options: [Err(UnknownId(34))] }, ref icmpv4 = Icmpv4Header { icmp_type: TimestampReply(TimestampMessage { id: 54195, seq: 33654, originate_timestamp: 2593543617, receive_timestamp: 534962444, transmit_timestamp: 141913819 }), checksum: 50019 }, ref icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 228, code_u8: 213, bytes5to8: [17, 44, 158, 162] }, checksum: 51305 }, ref payload = [176, 206, 197, 85, 12, 15, 112, 1, 92, 102, 232, 123, 66, 67, 0, 129, 111, 164, 134, 24, 82, 206, 103, 137, 239, 130, 78, 149, 131, 220, 160, 114, 222, 169, 165, 141, 202, 80, 8, 234, 94, 151, 21, 242, 120, 93, 230, 85, 162, 209, 105, 154, 72, 203, 198, 235, 64, 239, 33, 102, 54, 45, 201, 245, 26, 192, 182, 10, 232, 131, 82, 9, 32, 183, 65, 225, 132, 208, 61, 251, 109, 66, 234, 46, 65, 240, 148, 46, 146, 56, 17, 205, 103, 253, 158, 32, 21, 148, 243, 191, 23, 135, 145, 188, 136, 139, 125, 99, 144, 34, 142, 229, 128, 46, 226, 88, 205, 126, 2, 39, 87, 16, 74, 20, 184, 165, 75, 34, 0, 206, 61, 220, 196, 39, 190, 113, 217, 4, 238, 26, 232, 52, 18, 123, 48, 196, 238, 75, 120, 241, 41, 229, 114, 161, 65, 143, 237, 251, 87, 156, 155, 210, 178, 43, 166, 184, 11, 9, 250, 221, 22, 72, 65, 160, 116, 60, 242, 239, 97, 249, 39, 207, 214, 47, 6, 120, 51, 165, 69, 122, 156, 142, 159, 27, 224, 171, 233, 105, 79, 49, 32, 118, 141, 227, 174, 207, 109, 135, 5, 13, 248, 235, 33, 113, 233, 53, 131, 52, 188, 52, 203, 12, 88, 54, 84, 21, 132, 41, 211, 30, 215, 46, 108, 126, 141, 13, 113, 21, 233, 111, 115, 109, 107, 246, 214, 65, 211, 186, 60, 224, 211, 214, 191, 65, 62, 169, 122, 246, 237, 107, 183, 160, 179, 144, 106, 63, 10, 0, 87, 75, 175, 228, 178, 219, 35, 227, 161, 214, 134, 106, 156, 244, 126, 186, 201, 199, 202, 30, 220, 163, 146, 208, 192, 179, 241, 219, 6, 43, 39, 21, 231, 16, 213, 192, 194, 82, 33, 121, 188, 56, 108, 79, 219, 183, 20, 18, 192, 42, 7, 109, 217, 25, 42, 170, 154, 206, 35, 131, 193, 187, 217, 185, 178, 196, 130, 25, 85, 228, 103, 112, 163, 53, 154, 65, 68, 219, 219, 163, 208, 44, 33, 90, 118, 133, 114, 43, 242, 58, 196, 246, 55, 223, 181, 14, 249, 35, 73, 179, 242, 211, 188, 156, 4, 213, 54, 205, 50, 83, 116, 13, 128, 133, 239, 122, 106, 98, 140, 171, 202, 8, 11, 51, 219, 68, 19, 114, 8, 229, 177, 199, 9, 228, 130, 194, 211, 59, 16, 145, 23, 163, 228, 186, 187, 24, 194, 93, 75, 44, 23, 192, 96, 226, 164, 242, 75, 135, 48, 118, 108, 49, 62, 63, 228, 71, 153, 134, 15, 192, 249, 103, 44, 211] cc 19938c0e61de8fbe9f8df17d1325091a1825e2b209a4adb8b21dcd28a0e0f558 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0 }, ref vlan_outer = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref vlan_inner = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref ipv4 = Ipv4Header { ihl: 8, differentiated_services_code_point: 0, explicit_congestion_notification: 0, payload_len: 34240, identification: 0, dont_fragment: false, more_fragments: false, fragments_offset: 0, time_to_live: 0, protocol: 95, header_checksum: 2458, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [80, 229, 92, 224, 82, 126, 48, 60, 105, 201, 96, 77] }, ref ipv4_exts = Ipv4Extensions { auth: None }, ref ipv6 = Ipv6Header { traffic_class: 129, flow_label: 787898, payload_length: 54827, next_header: 33, hop_limit: 254, source: [109, 7, 4, 79, 149, 61, 253, 73, 214, 117, 64, 10, 168, 230, 137, 73], destination: [44, 199, 106, 47, 71, 14, 18, 94, 107, 95, 41, 238, 83, 187, 218, 132] }, ref ipv6_exts = Ipv6Extensions { hop_by_hop_options: Some(Ipv6RawExtensionHeader { next_header: 60, payload: [112, 231, 1, 88, 255, 168, 119, 95, 144, 149, 61, 29, 235, 11, 182, 192, 83, 15, 201, 180, 189, 232, 85, 231, 220, 116, 192, 132, 43, 162, 23, 161, 129, 246, 28, 236, 164, 174, 67, 235, 121, 212, 9, 73, 30, 98, 190, 173, 122, 133, 58, 154, 142, 6, 24, 203, 3, 230, 232, 50, 77, 203, 83, 151, 3, 157, 193, 242, 25, 246, 224, 4, 178, 173, 156, 5, 210, 3, 97, 27, 171, 152, 187, 16, 98, 73, 57, 176, 35, 25, 246, 71, 154, 32, 132, 227, 164, 29, 92, 159, 74, 247, 144, 68, 39, 254, 227, 156, 63, 140, 246, 246, 199, 111, 101, 173, 179, 116, 79, 114, 249, 162, 71, 113, 121, 224, 229, 237, 67, 3, 4, 162, 152, 120, 58, 132, 244, 196, 136, 196, 206, 160, 45, 83, 167, 218, 32, 206, 52, 246, 144, 220, 133, 150, 36, 91, 193, 118, 28, 33, 236, 64, 255, 72, 190, 70, 160, 38, 139, 134, 80, 153, 236, 93, 198, 211, 21, 19, 251, 131, 119, 219, 161, 19, 144, 96, 6, 188, 115, 43, 91, 216, 5, 135, 101, 166, 99, 11, 174, 169, 255, 248, 101, 23, 62, 55, 169, 40, 6, 186, 195, 235, 76, 41] }), destination_options: Some(Ipv6RawExtensionHeader { next_header: 43, payload: [238, 203, 236, 202, 32, 25, 193, 164, 167, 189, 30, 208, 207, 108, 114, 10, 12, 226, 180, 59, 207, 44, 143, 244, 221, 200, 232, 154, 140, 180, 167, 70, 197, 72, 31, 249, 141, 75, 7, 255, 201, 53, 76, 234, 201, 187, 214, 141, 249, 216, 232, 12, 45, 196, 208, 110, 78, 14, 60, 251, 17, 239, 13, 141, 216, 29, 230, 120, 102, 88, 104, 237, 17, 252, 108, 126, 203, 75] }), routing: Some(Ipv6RoutingExtensions { routing: Ipv6RawExtensionHeader { next_header: 44, payload: [254, 77, 166, 70, 182, 207, 149, 153, 212, 40, 122, 249, 15, 84, 41, 126, 254, 103, 2, 162, 52, 216, 226, 175, 148, 253, 5, 153, 50, 16, 32, 44, 139, 24, 73, 245, 17, 9, 50, 18, 176, 70, 177, 29, 220, 255, 253, 255, 94, 39, 69, 225, 93, 176, 139, 48, 98, 210, 151, 80, 3, 105, 114, 59, 232, 171, 163, 235, 40, 56, 9, 85, 180, 225, 71, 230, 216, 128, 194, 109, 150, 198, 175, 68, 186, 112, 223, 48, 61, 245, 191, 34, 3, 207, 250, 27, 110, 21, 229, 221, 166, 76, 220, 214, 215, 104, 137, 46, 134, 94, 106, 89, 129, 218, 113, 234, 119, 79, 84, 147, 98, 202, 148, 239, 67, 99, 223, 222, 139, 13, 237, 170, 164, 89, 15, 185, 202, 252, 2, 156, 33, 28, 194, 52, 180, 232, 239, 202, 23, 123, 215, 81, 236, 65, 80, 192, 136, 184, 237, 135, 205, 183, 104, 66, 253, 128, 176, 245, 213, 65, 120, 202, 15, 130, 202, 55, 28, 94, 189, 8, 11, 59, 112, 96, 196, 186, 15, 96, 32, 60, 193, 8, 95, 44, 110, 224, 32, 71, 96, 140, 69, 124, 69, 241, 153, 87, 65, 15, 171, 113, 248, 239, 156, 78, 174, 47, 99, 190, 159, 163, 29, 197, 75, 161, 4, 209, 213, 236, 86, 120, 74, 15, 147, 85, 135, 147, 242, 220, 144, 55, 202, 170, 71, 90, 107, 103, 170, 8, 231, 169, 231, 170, 153, 184, 158, 99, 127, 228, 243, 191, 139, 69, 75, 133, 185, 212, 104, 214, 233, 171, 0, 135, 73, 14, 31, 2, 90, 187, 82, 205, 161, 69, 251, 143, 243, 15, 56, 250, 98, 175, 82, 196, 216, 95, 249, 127, 84, 181, 211, 50, 81, 36, 26, 247, 224, 3, 92, 61, 120, 67, 163, 170, 185, 61, 254, 91, 248, 20, 150, 19, 49, 71, 52, 102, 152, 209, 105, 219, 65, 151, 19, 101, 102, 133, 216, 94, 237, 221, 232, 168, 51, 28, 214, 231, 179, 180, 235, 17, 36, 19, 33, 54, 232, 131, 150, 95, 96, 84, 13, 6, 20, 28, 160, 92, 193, 206, 231, 10, 238, 240, 6, 77, 44, 78, 6, 253, 142, 54, 72, 135, 39, 144, 95, 132, 194, 5, 25, 225, 46, 143, 153, 93, 213, 32, 114, 214, 230, 61, 21, 189, 86, 34, 12, 85, 75, 242, 112, 3, 251, 4, 129, 141, 153, 47, 228, 157, 65, 13, 82, 38, 80, 34, 7, 52, 172, 210, 141, 83, 27, 39, 100, 16, 0, 216, 114, 134, 195, 220, 156, 79, 174, 220, 88, 252, 193, 210, 93, 190, 229, 6, 16, 63, 190, 46, 5, 126, 28, 10, 51, 102, 19, 8, 153, 157, 142, 125, 6, 40, 100, 68, 139, 231, 69, 159, 46, 98, 36, 25, 200, 140, 107, 101, 15, 70, 25, 89, 211, 3, 17, 253, 9, 50, 39, 60, 47, 185, 135, 17, 218, 116, 65, 107, 110, 122, 227, 202, 155, 71, 164, 119, 189, 84, 128, 8, 180, 93, 177, 45, 15, 198, 16, 79, 179, 46, 103, 85, 91, 229, 254, 12, 152, 129, 160, 104, 16, 217, 157, 157, 61, 137, 189, 194, 132, 234, 243, 123, 91, 70, 132, 5, 222, 200, 134, 26, 129, 182, 254, 254, 151, 165, 184, 13, 85, 106, 44, 20, 79, 183, 130, 223, 209, 88, 35, 174, 160, 91, 199, 118, 168, 40, 189, 181, 59, 38, 74, 43, 24, 80, 25, 224, 73, 119, 241, 101, 41, 109, 115, 24, 35, 204, 181, 100, 33, 78, 109, 253, 192, 21, 137, 4, 203, 143, 243, 152, 96, 237, 209, 26, 217, 68, 239, 59, 1, 200, 219, 177, 22, 196, 180, 1, 102, 202, 126, 216, 32, 221, 143, 99, 223, 7, 129, 183, 252, 35, 59, 15, 204, 56, 18, 118, 229, 215, 81, 147, 172, 69, 116, 46, 51, 169, 157, 22, 69, 178, 97, 224, 190, 198, 11, 216, 188, 108, 161, 120, 196, 181, 172, 21, 41, 124, 197, 106, 58, 193, 102, 16, 67, 127, 109, 45, 135, 60, 110, 30, 155, 88, 173, 34, 14, 78, 117, 93, 158, 51, 117, 168, 226, 43, 44, 173, 185, 20, 111, 151, 32, 95, 226, 103, 101, 76, 229, 117, 14, 56, 187, 185, 131, 185, 50, 68, 20, 173, 69, 94, 131, 252, 114, 133, 98, 55, 143, 45, 12, 25, 226, 189, 170, 73, 70, 163, 98, 27, 195, 211, 38, 108, 243, 46, 5, 140, 56, 85, 136, 98, 154, 22, 112, 91, 192, 81, 51, 252, 190, 222, 16, 151, 178, 51, 209, 208, 15, 72, 17, 127, 219, 117, 10, 93, 193, 133, 55, 125, 98, 95, 35, 63, 115, 88, 44, 80, 120, 10, 224, 207, 98, 243, 227, 236, 149, 9, 163, 166, 250, 134, 32, 144, 182, 144, 212, 237, 231, 157, 18, 39, 46, 116, 226, 106, 195, 193, 129, 171, 121, 5, 135, 72, 160, 170, 139, 83, 138, 70, 124, 115, 12, 219, 197, 250, 209, 205, 250, 55, 107, 37, 26, 107, 141, 164, 107, 93, 45, 26, 7, 240, 168, 25, 169, 241, 21, 22, 142, 216, 164, 17, 50, 214, 204, 32, 31, 184, 179, 11, 134, 255, 229, 160, 130, 167, 149, 190, 141, 191, 64, 247, 35, 182, 183, 9, 119, 116, 199, 43, 91, 48, 101, 117, 52, 145, 248, 62, 25, 82, 129, 253, 53, 206, 51, 195, 80, 45, 83, 239, 194, 4, 108, 177, 156, 196, 42, 215, 45, 2, 2, 251, 9, 122, 230, 239, 39, 83, 129, 88, 192, 181, 57, 235, 22, 25, 122, 54, 9, 242, 32, 96, 178, 29, 2, 9, 212, 157, 250, 227, 114, 138, 238, 202, 121, 90, 101, 42, 137, 159, 27, 112, 225, 206, 201, 104, 201, 177, 177, 26, 103, 227, 100, 190, 231, 117, 136, 230, 180, 121, 54, 60, 113, 26, 49, 140, 66, 76, 150, 183, 116, 193, 170, 130, 166, 214, 204, 212, 125, 75, 19, 17, 79, 245, 198, 176, 15, 17, 43, 92, 169, 227, 25, 11, 194, 245, 93, 126, 247, 254, 74, 148, 187, 231, 153, 196, 193, 177, 125, 67, 183, 79, 219, 77, 89, 233, 42, 45, 38, 232, 164, 146, 228, 179, 204, 107, 191, 254, 232, 61, 172, 148, 144, 56, 60, 178, 90, 211, 72, 255, 93, 3, 25, 220, 180, 82, 70, 85, 209, 97, 92, 7, 232, 204, 201, 202, 235, 31, 75, 60, 157, 149, 147, 168, 175, 138, 116, 118, 127, 123, 98, 115, 205, 37, 81, 74, 136, 150, 89, 83, 204, 201, 105, 154, 27, 1, 104, 193, 102, 17, 247, 204, 236, 134, 110, 165, 141, 123, 21, 229, 56, 215, 184, 3, 251, 7, 181, 246, 50, 133, 74, 50, 36, 224, 12, 171, 200, 245, 193, 110, 42, 93, 115, 215, 182, 128, 107, 175, 64, 170, 131, 206, 74, 124, 194, 150, 191, 102, 85, 139, 127, 117, 35, 239, 137, 225, 68, 108, 118, 250, 127, 250, 128, 167, 149, 240, 21, 238, 117, 98, 181, 186, 162, 83, 152, 255, 80, 111, 235, 55, 133, 209, 43, 118, 151, 148, 140, 253, 249, 178, 148, 174, 254, 236, 250, 172, 27, 220, 189, 20, 26, 201, 253, 187, 109, 55, 51, 26, 243, 44, 65, 59, 131, 116, 15, 52, 222, 174, 63, 49, 150, 113, 71, 98, 228, 48, 27, 236, 183, 240, 184, 87, 21, 146, 248, 224, 54, 46, 81, 109, 129, 243, 104, 48, 239, 36, 8, 232, 9, 229, 82, 164, 3, 186, 86, 202, 128, 224, 218, 19, 161, 92, 187, 55, 41, 203, 143, 139, 54, 50, 120, 253, 62, 26, 232, 113, 97, 136, 6, 53, 89, 90, 200, 202, 246, 102, 193, 14, 244, 179, 226, 253, 205, 189, 236, 98, 51, 154, 217, 83, 254, 238, 229, 32, 197, 124, 71, 165, 235, 224, 67, 190, 207, 23, 232, 240, 34, 203, 137, 64, 93, 65, 240, 205, 71, 61, 36, 104, 99, 125, 94, 9, 255, 131, 204, 210, 17, 210, 205, 112, 188, 146, 246, 237, 76, 128, 24, 198, 43, 184, 72, 22, 77, 196, 8, 77, 138, 105, 155, 165, 215, 253, 162, 248, 172, 95, 79, 102, 199, 90, 251, 122, 74, 24, 69, 65, 112, 172, 227, 140, 202, 104, 235, 119, 220, 80, 78, 234, 21, 129, 138, 250, 188, 87, 131, 20, 185, 76, 24, 103, 231, 145, 48, 207, 167, 230, 18, 30, 80, 190, 139, 36, 22, 165, 21, 176, 240, 227, 82, 246, 112, 184, 21, 226, 116, 175, 147, 250, 109, 236, 83, 52, 112, 156, 180, 111, 220, 43, 77, 112, 98, 193, 125, 145, 31, 38, 115, 213, 67, 95, 62, 81, 208, 123, 8, 158, 157, 171, 133, 246, 210, 56, 169, 221, 27, 153, 121, 210, 134, 24, 202, 90, 183, 78, 229, 99, 153, 245, 135, 122, 55, 158, 129, 216, 147, 80, 150, 203, 182, 220, 9, 95, 65, 222, 120, 144, 133, 148, 45, 134, 7, 113, 74, 219, 238, 229, 1, 112, 173, 189, 232, 176, 219, 14, 143, 14, 134, 108, 209, 218, 59, 252, 192, 185, 255, 142, 96, 87, 1, 77, 243, 219, 46, 78, 253, 128, 249, 182, 149, 144, 174, 176, 198, 64, 3, 200, 129, 217, 102, 131, 119, 102, 74, 10, 212, 86, 143, 165, 108, 235, 36, 100, 18, 3, 241, 8, 113, 92, 201, 114, 216, 97, 120, 199, 196, 172, 29, 179, 205, 252, 163, 199, 187, 139, 42, 103, 99, 51, 51, 8, 205, 180, 149, 177, 245, 77, 111, 26, 246, 112, 174, 236, 221, 168, 72, 137, 38, 59, 10, 89, 6, 68, 66, 158, 17, 246, 149, 239, 165, 221, 28, 144, 252, 247, 102, 194, 215, 90, 15, 206, 93, 133, 197, 15, 81, 155, 143, 200, 201, 112, 105, 60, 84, 52, 179, 179, 18, 67, 178, 126, 113, 15, 45, 26, 159, 223, 161, 249, 141, 31, 179, 43, 94, 8, 125, 194, 219, 26, 65, 57, 166, 236, 185, 24, 63, 206, 215, 22, 85, 117, 41, 197, 182, 147, 46, 202, 167, 206, 154, 89, 200, 95, 238, 93, 125, 4, 101, 195, 253, 179, 29, 13, 234, 225, 171, 72, 82, 224, 60, 191, 74, 113, 217, 161, 10, 13, 202, 196, 144, 104, 46, 71, 49, 212, 22, 181, 250, 28, 27, 95, 151, 158, 25, 84, 226, 200] }, final_destination_options: None }), fragment: Some(Ipv6FragmentHeader { next_header: 109, fragment_offset: 2113, more_fragments: true, identification: 5944605 }), auth: None }, ref udp = UdpHeader { source_port: 27523, destination_port: 52161, length: 45869, checksum: 14910 }, ref tcp = TcpHeader { source_port: 17245, destination_port: 46697, sequence_number: 160328470, acknowledgment_number: 2631620014, data_offset: 10, ns: false, fin: false, syn: false, rst: true, psh: false, ack: true, urg: false, ece: true, cwr: false, window_size: 24158, checksum: 53442, urgent_pointer: 8968, options: [Err(UnknownId(173))] }, ref icmpv4 = Icmpv4Header { icmp_type: Unknown { type_u8: 234, code_u8: 221, bytes5to8: [200, 89, 56, 131] }, checksum: 16430 }, ref icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 30, code_u8: 106, bytes5to8: [52, 110, 228, 155] }, checksum: 38251 }, ref payload = [111, 188, 151, 183, 149, 185, 18, 245, 219, 34, 101, 100, 224, 105, 138, 24, 34, 92, 6, 75, 219, 201, 60, 187, 214, 136, 150, 248, 6, 50, 64, 136, 89, 13, 42, 46, 93, 80, 5, 22, 114, 77, 34, 58, 115, 121, 159, 158, 151, 132, 171, 188, 57, 49, 52, 166, 160, 191, 60, 116, 6, 117, 215, 53, 99, 85, 33, 16, 109, 90, 48, 192, 31, 77, 71, 43, 229, 66, 22, 199, 176, 216, 156, 180, 197, 105, 72, 60, 198, 61, 119, 201, 118, 240, 131, 5, 102, 75, 200, 84, 254, 216, 228, 209, 150, 251, 234, 232, 20, 243, 127, 121, 97, 68, 16, 43, 140, 15, 235, 75, 178, 41, 209, 114, 244, 16, 163, 224, 223, 132, 128, 56, 142, 160, 184, 140, 89, 35, 167, 84, 217, 209, 200, 3, 120, 124, 220, 113, 169, 39, 64, 82, 255, 81, 239, 172, 199, 48, 179, 102, 109, 53, 167, 253, 203, 114, 225, 103, 233, 1, 72, 29, 178, 90, 44, 246, 248, 43, 137, 46, 5, 250, 25, 94, 155, 183, 46, 229, 121, 120, 16, 105, 40, 15, 168, 29, 93, 71, 42, 36, 179, 253, 67, 132, 81, 196, 190, 165, 130, 54, 57, 212, 240, 76, 252, 175, 147, 200, 18, 179, 196, 82, 9, 135, 197, 217, 12, 60, 130, 144, 129, 206, 133, 122, 183, 87, 194, 149, 79, 206, 67, 178, 51, 38, 60, 143, 132, 9, 221, 193, 27, 31, 145, 245, 137, 134, 248, 231, 68, 211, 125, 22, 234, 78, 231, 119, 27, 241, 143, 43, 173, 231, 117, 180, 255, 230, 138, 68, 233, 225, 184, 16, 132, 168, 65, 84, 177, 210, 183, 55, 188, 216, 82, 7, 137, 1, 81, 69, 14, 104, 82, 239, 73, 218, 70, 196, 163, 59, 183, 151, 95, 197, 81, 49, 97, 162, 96, 9, 95, 254, 137, 252, 100, 190, 218, 124, 130, 82, 32, 154, 253, 44, 253, 58, 149, 116, 45, 82, 104, 103, 119, 42, 175, 208, 203, 25, 65, 154, 218, 222, 22, 148, 94, 5, 226, 217, 158, 148, 30, 84, 36, 142, 214, 166, 176, 62, 198, 178, 94, 205, 220, 155, 5, 86, 48, 167, 114, 108, 210, 127, 105, 247, 106, 30, 77, 100, 149, 109, 139, 60, 174, 121, 24, 203, 35, 163, 15, 212, 151, 206, 94, 134, 28, 253, 192, 66, 12, 167, 45, 146, 101] cc 8a9b0a970a9a2fe38d925b7ce0f4ff73ba262d7258e2d20e1ff7c9251885a396 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0x0000 }, ref vlan_outer = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan_inner = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0806 (Address Resolution Protocol (ARP)) }, ref ipv4 = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 20, identification: 0, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(0), time_to_live: 0, protocol: 29 (ISO-TP4 - ISO Transport Protocol Class 4), header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ref ipv4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 24 (TRUNK-2 - Trunk-2), spi: 2509063036, sequence_number: 3755394165, raw_icv: [92, 181, 195, 73, 130, 142, 140, 140, 223, 45, 197, 36, 9, 69, 55, 43, 149, 231, 9, 4, 120, 102, 160, 165, 107, 87, 64, 204, 231, 179, 197, 165, 115, 145, 144, 125, 94, 30, 168, 176, 19, 63, 236, 79, 115, 199, 15, 92, 162, 106, 139, 90, 248, 240, 127, 37, 144, 179, 147, 224, 57, 41, 152, 198, 117, 161, 199, 197, 98, 23, 220, 31, 79, 25, 99, 100, 171, 127, 4, 248, 29, 2, 189, 178, 206, 124, 247, 24, 51, 75, 240, 246, 123, 220, 42, 147, 91, 187, 218, 92, 90, 171, 47, 1, 54, 19, 58, 73, 82, 207, 143, 83, 100, 150, 44, 228, 214, 246, 111, 239, 107, 35, 187, 109, 244, 118, 33, 58, 192, 37, 195, 191, 74, 82, 149, 2, 25, 245, 127, 172, 34, 72, 200, 6, 37, 121, 147, 136, 76, 40, 209, 25, 202, 208, 118, 230, 122, 151, 38, 140, 238, 68, 101, 149, 46, 233, 83, 180, 105, 169, 213, 123, 189, 109, 9, 214, 192, 238, 95, 177, 223, 71, 103, 7, 243, 54, 156, 100, 138, 195, 151, 91, 193, 151, 177, 82, 165, 16, 106, 253, 90, 76, 0, 204, 20, 190, 229, 54, 147, 227, 190, 57, 233, 143, 98, 114, 82, 106, 157, 134, 187, 18, 66, 21, 220, 135, 175, 76, 33, 91, 242, 181, 47, 74, 206, 252, 84, 28, 248, 36, 94, 8, 124, 219, 150, 145, 213, 192, 233, 11, 56, 72, 201, 140, 48, 93, 198, 77, 26, 31, 208, 179, 191, 185, 195, 83, 39, 3, 110, 92, 146, 229, 222, 36, 53, 176, 178, 49, 44, 110, 75, 8, 104, 135, 52, 26, 50, 4, 183, 70, 84, 67, 8, 76, 25, 45, 106, 225, 33, 107, 142, 35, 90, 100, 141, 25, 202, 82, 41, 43, 49, 103, 33, 202, 244, 125, 123, 69, 118, 118, 118, 72, 244, 53, 71, 161, 133, 88, 131, 144, 109, 3, 60, 40, 166, 109, 206, 115, 18, 97, 172, 157, 50, 144, 179, 3, 7, 154, 121, 56, 138, 174, 242, 151, 95, 245, 204, 195, 241, 62, 200, 106, 28, 205, 27, 201, 190, 66, 12, 127, 70, 30, 227, 173, 225, 6, 142, 228, 215, 244, 138, 130, 171, 59, 180, 180, 127, 145, 1, 228, 40, 222, 36, 43, 141, 74, 226, 229, 46, 9, 101, 229, 99, 111, 102, 188, 153, 86, 118, 111, 1, 235, 176, 218, 83, 130, 14, 60, 142, 42, 26, 99, 245, 73, 213, 247, 185, 176, 118, 29, 139, 236, 218, 167, 13, 129, 124, 105, 194, 63, 95, 95, 197, 97, 65, 41, 221, 32, 122, 48, 218, 198, 95, 95, 124, 6, 2, 84, 160, 96, 128, 125, 172, 240, 53, 131, 49, 10, 254, 166, 16, 127, 66, 232, 206, 172, 31, 24, 115, 213, 172, 29, 96, 116, 88, 163, 135, 121, 233, 223, 63, 65, 45, 18, 119, 145, 192, 246, 94, 235, 25, 83, 174, 244, 215, 135, 229, 230, 216, 101, 146, 157, 118, 91, 43, 16, 196, 2, 167, 72, 139, 20, 105, 112, 167, 191, 83, 233, 43, 43, 254, 199, 49, 68, 69, 91, 192, 153, 244, 15, 59, 119, 80, 51, 129, 220, 154, 172, 193, 147, 59, 241, 215, 70, 224, 110, 21, 33, 34, 216, 177, 174, 227, 84, 38, 181, 249, 95, 87, 152, 165, 132, 95, 134, 37, 228] }) }, ref ipv6 = Ipv6Header { traffic_class: 15, flow_label: Ipv6FlowLabel(377208), payload_length: 39375, next_header: 11 (NVP-II - Network Voice Protocol), hop_limit: 164, source: [130, 247, 84, 229, 201, 228, 210, 155, 39, 71, 113, 56, 54, 232, 243, 221], destination: [42, 1, 131, 202, 153, 22, 76, 246, 14, 164, 20, 54, 68, 234, 189, 145] }, ref ipv6_exts = Ipv6Extensions { hop_by_hop_options: Some(Ipv6RawExtHeader { next_header: 60 (IPv6-Opts - Destination Options for IPv6), payload: [62, 47, 61, 126, 20, 254, 36, 110, 35, 19, 117, 163, 115, 86, 52, 46, 27, 154, 128, 33, 173, 71, 49, 222, 253, 62, 14, 57, 125, 78, 228, 70, 248, 103, 253, 195, 35, 6, 89, 220, 194, 161, 100, 221, 94, 202, 232, 176, 37, 104, 84, 204, 136, 200, 112, 93, 234, 1, 245, 194, 81, 94, 114, 17, 71, 224, 146, 87, 167, 234, 170, 48, 69, 136, 130, 154, 143, 122, 81, 104, 95, 50, 213, 82, 77, 182, 60, 31, 192, 201, 244, 231, 252, 125, 130, 59, 128, 141, 185, 41, 125, 190, 138, 143, 72, 81, 15, 3, 56, 216, 52, 209, 87, 197, 249, 92, 165, 226, 237, 205, 210, 3, 182, 13, 89, 244, 39, 149, 201, 75, 104, 25, 209, 124, 87, 125, 44, 51, 84, 70, 144, 225, 60, 8, 220, 2, 47, 131, 62, 183, 243, 165, 43, 20, 222, 245, 223, 171, 74, 184, 83, 63, 66, 200, 185, 58, 27, 172, 193, 98, 113, 56, 199, 151, 136, 20, 241, 232, 212, 165, 82, 187, 209, 172, 128, 153, 175, 140, 151, 178, 207, 210, 232, 57, 124, 187, 90, 140, 2, 203, 59, 81, 175, 81, 242, 125, 217, 19, 67, 109, 148, 37, 253, 127, 22, 118, 105, 86, 190, 137, 172, 53, 96, 87, 166, 105, 151, 59, 192, 91, 76, 58, 186, 14, 61, 133, 213, 36, 178, 48, 71, 226, 35, 225, 153, 252, 43, 154, 118, 6, 58, 243, 47, 180, 225, 73, 137, 234, 81, 56, 91, 175, 90, 151, 177, 210, 6, 73, 250, 9, 158, 217, 181, 141, 37, 184, 233, 148, 143, 13, 110, 161, 112, 191, 234, 14, 69, 54, 88, 227, 6, 43, 248, 36, 191, 157, 200, 209, 170, 189, 46, 203, 128, 37, 247, 62, 31, 116, 145, 11, 91, 150, 186, 252, 188, 28, 195, 202, 242, 240, 5, 204, 27, 102, 36, 94, 55, 244, 238, 9, 199, 126, 145, 192, 77, 88, 154, 197, 74, 186, 94, 81, 172, 19, 213, 130, 78, 98, 160, 53, 199, 70, 150, 17, 167, 36, 138, 239, 164, 89, 252, 210, 188, 230, 252, 180, 138, 105, 82, 71, 221, 160, 48, 40, 54, 168, 39, 153, 124, 210, 125, 202, 57, 145, 202, 186, 49, 218, 153, 18, 230, 234, 222, 250, 125, 160, 11, 228, 185, 111, 15, 183, 24, 54, 112, 220, 188, 185, 132, 199, 20, 9, 81, 53, 195, 100, 208, 40, 167, 241, 8, 215, 70, 112, 245, 242, 210, 225, 54, 113, 80, 45, 123, 44, 8, 54, 64, 21, 176, 55, 26, 47, 197, 145, 107, 144, 28, 110, 182, 27, 0, 1, 47, 166, 244, 61, 0, 10, 116, 36, 106, 85, 214, 49, 37, 215, 25, 113, 22, 34, 49, 194, 206, 54, 75, 250, 86, 72, 15, 180, 147, 202, 211, 42, 192, 83, 95, 133, 78, 13, 26, 51, 176, 183, 149, 132, 51, 13, 173, 190, 205, 236, 235, 28, 244, 110, 185, 136, 10, 52, 244, 24, 23, 202, 51, 101, 139, 150, 79, 118, 138, 55, 60, 90, 146, 20, 136, 15, 253, 103, 24, 159, 155, 229, 119, 121, 21, 60, 168, 62, 95, 95, 46, 230, 165, 224, 255, 68, 80, 233, 89, 185, 215, 238, 33, 186, 174, 233, 170, 65, 205, 66, 127, 30, 85, 119, 248, 136, 163, 213, 15, 159, 132, 160, 58, 4, 155, 47, 52, 166, 16, 195, 116, 156, 167, 25, 125, 160, 101, 29, 96, 11, 191, 192, 20, 96, 166, 203, 40, 233, 187, 197, 199, 27, 21, 152, 63, 26, 61, 148, 11, 101, 175, 137, 233, 155, 41, 178, 106, 177, 178, 138, 80, 93, 114, 47, 24, 166, 30, 109, 48, 54, 243, 44, 214, 250, 146, 62, 145, 212, 244, 227, 10, 68, 35, 137, 172, 242, 219, 175, 173, 66, 92, 87, 110, 244, 41, 10, 67, 161, 217, 164, 140, 199, 197, 195, 20, 165, 89, 133, 9, 128, 171, 67, 127, 142, 207, 60, 0, 69, 127, 52, 26, 222, 36, 254, 236, 223, 172, 209, 155, 199, 13, 241, 158, 249, 196, 185, 173, 37, 195, 94, 15, 183, 129, 51, 81, 28, 244, 179, 196, 200, 248, 33, 72, 202, 238, 176, 25, 197, 83, 42, 224, 214, 41, 11, 187, 211, 100, 57, 245, 80, 135, 213, 174, 58, 16, 1, 98, 13, 162, 178, 13, 61, 55, 165, 150, 209, 62, 115, 88, 178, 168, 2, 245, 21, 231, 221, 84, 131, 243, 0, 99, 193, 157, 227, 244, 41, 47, 127, 151, 23, 30, 83, 189, 104, 239, 97, 111, 79, 213, 165, 19, 75, 237, 148, 161, 132, 81, 29, 197, 193, 199, 119, 189, 102, 227, 122, 38, 110, 186, 178, 234, 41, 116, 127, 123, 65, 110, 128, 171, 65, 10, 156, 131, 186, 49, 112, 22, 189, 222, 27, 95, 65, 233, 51, 151, 52, 43, 185, 171, 194, 108, 200, 196, 8, 77, 238, 81, 37, 195, 19, 220, 248, 177, 227, 141, 76, 1, 150, 158, 187, 133, 49, 147, 64, 151, 48, 212, 97, 241, 153, 118, 8, 253, 119, 125, 8, 136, 228, 54, 174, 147, 63, 68, 219, 79, 5, 56, 43, 16, 144, 47, 79, 171, 180, 66, 142, 186, 132, 44, 50, 172, 44, 118, 96, 150, 161, 174, 218, 68, 217, 94, 113, 33, 49, 116, 13, 161, 46, 48, 206, 28, 53, 200, 208, 243, 104, 97, 204, 186, 126, 153, 70, 141, 149, 28, 189, 206, 100, 101, 179, 183, 106, 15, 243, 49, 90, 119, 183, 222, 49, 244, 50, 218, 16, 186, 19, 153, 141, 2, 210, 227, 45, 191, 60, 50, 226, 254, 160, 109, 3, 24, 189, 138, 220, 230, 73, 51, 61, 238, 228, 242, 183, 71, 166, 59, 183, 32, 223, 40, 233, 218, 48, 2, 215, 103, 222, 229, 8, 134, 145, 136, 143, 66, 139, 95, 212, 59, 173, 225, 105, 138, 189, 234, 113, 174, 7, 232, 207, 226, 74, 50, 247, 212, 76, 237, 177, 103, 145, 92, 3, 41, 13, 139, 149, 187, 128, 43, 202, 130, 94, 25, 82, 250, 220, 192, 164, 148, 221, 98, 94, 105, 178, 60, 20, 142, 231, 23, 76, 136, 12, 85, 180, 85, 75, 134, 195, 147, 194, 211, 59, 183, 85, 127, 123, 195, 153, 201, 197, 37, 212, 160, 39, 68, 229, 244, 71, 79, 248, 77, 84, 130, 71, 155, 4, 141, 158, 211, 173, 62, 87, 146, 15, 3, 122, 154, 175, 34, 158, 255, 137, 84, 218, 30, 59, 182, 59, 193, 92, 13, 174, 243, 178, 227, 194, 228, 41, 12, 121, 71, 170, 15, 201, 10, 155, 108, 48, 122, 69, 246, 158, 192, 155, 67, 100, 254, 16, 151, 85, 69, 24, 6, 254, 13, 94, 134, 144, 16, 5, 112, 80, 235, 159, 73, 15, 87, 114, 216, 36, 8, 168, 11, 215, 188, 210, 243, 85, 78, 96, 27, 242, 83, 32, 207, 27, 191, 176, 75, 76, 50, 200, 104, 154, 228, 203, 31, 232, 105, 79, 78, 89, 63, 146, 52, 108, 114, 8, 186, 201, 229, 162, 179, 214, 46, 177, 173, 146, 84, 73, 21, 79, 22, 137, 187, 240, 72, 47, 88, 165, 195, 244, 133, 27, 252, 218, 149, 50, 231, 92, 253, 198, 255, 51, 214, 53, 147, 189, 100, 71, 174, 39, 17, 247, 177, 185, 8, 181, 160, 139, 211, 223, 172, 33, 73, 116, 108, 182, 248, 82, 39, 215, 136, 26, 141, 159, 226, 129, 99, 232, 155, 174, 28, 115, 117, 119, 231, 30, 173, 235, 156, 13, 161, 1, 52, 174] }), destination_options: Some(Ipv6RawExtHeader { next_header: 44 (IPv6-Frag - Fragment Header for IPv6), payload: [32, 35, 195, 46, 88, 189, 22, 199, 230, 162, 123, 89, 66, 218, 24, 103, 194, 32, 107, 137, 196, 98, 188, 82, 148, 190, 188, 13, 7, 214, 106, 129, 158, 134, 232, 135, 109, 7, 40, 241, 74, 227, 139, 81, 143, 182, 224, 235, 137, 14, 81, 157, 200, 186, 195, 164, 166, 184, 143, 252, 54, 118, 10, 11, 253, 83, 239, 71, 139, 127, 25, 28, 91, 186, 238, 170, 141, 38, 47, 164, 229, 109, 243, 71, 182, 25, 136, 223, 183, 40, 91, 105, 9, 169, 64, 149, 69, 103, 60, 81, 192, 20, 161, 114, 111, 222, 211, 64, 255, 79, 20, 28, 205, 59, 6, 58, 243, 42, 228, 120, 180, 47, 106, 177, 89, 25, 53, 159, 172, 159, 96, 119, 139, 68, 27, 234, 249, 37, 174, 0, 78, 138, 48, 181, 7, 58, 62, 134, 209, 152, 63, 7, 167, 33, 48, 0, 204, 240, 31, 135, 59, 245, 179, 144, 36, 71, 18, 25, 132, 86, 133, 135, 186, 30, 198, 189, 95, 143, 189, 141, 108, 134, 202, 110, 171, 178, 92, 230, 238, 255, 155, 7, 233, 217, 134, 190, 33, 127, 176, 90, 11, 146, 61, 11, 79, 196, 173, 41, 88, 125, 15, 124, 8, 68, 88, 145, 171, 125, 177, 115, 162, 157, 127, 171, 41, 209, 86, 179, 79, 69, 28, 197, 177, 11, 211, 170, 162, 45, 123, 46, 59, 112, 11, 132, 84, 167, 203, 152, 42, 204, 198, 204, 248, 88, 61, 195, 248, 241, 145, 19, 231, 207, 226, 95, 130, 216, 147, 124, 19, 2, 55, 69, 223, 30, 178, 166, 99, 76, 122, 6, 136, 36, 19, 123, 147, 144, 105, 205, 243, 85, 216, 117, 77, 151, 122, 1, 161, 20, 133, 47, 0, 23, 127, 205, 49, 146, 136, 12, 98, 52, 141, 4, 237, 120, 230, 103, 248, 181, 37, 105, 127, 1, 174, 183, 137, 187, 134, 119, 102, 124, 195, 229, 3, 111, 49, 203, 33, 22, 18, 110, 11, 163, 202, 92, 158, 15, 187, 172, 222, 135, 79, 72, 178, 135, 131, 248, 213, 21, 243, 106, 98, 144, 114, 194, 103, 175, 69, 182, 57, 15, 93, 146, 221, 86, 173, 19, 112, 155, 52, 74, 82, 104, 174, 125, 47, 203, 64, 77, 230, 90, 110, 37, 36, 254, 210, 96, 29, 161, 124, 195, 83, 164, 43, 200, 101, 65] }), routing: None, fragment: Some(Ipv6FragmentHeader { next_header: 51 (AH - Authentication Header), fragment_offset: IpFragOffset(40), more_fragments: false, identification: 936320599 }), auth: Some(IpAuthHeader { next_header: 50 (ESP - Encap Security Payload), spi: 1791434917, sequence_number: 265966117, raw_icv: [226, 4, 122, 88, 130, 221, 201, 80, 210, 94, 96, 230, 244, 224, 150, 80, 90, 103, 75, 80, 142, 100, 22, 49, 19, 18, 103, 246, 4, 136, 202, 81, 234, 216, 135, 16, 59, 70, 188, 72, 16, 69, 103, 133, 58, 104, 118, 132, 203, 4, 197, 196, 209, 61, 43, 216, 193, 158, 252, 235, 62, 176, 46, 22, 85, 121, 196, 55, 203, 99, 107, 202, 139, 163, 4, 43, 215, 57, 61, 135, 76, 79, 100, 179, 207, 129, 248, 136, 109, 67, 122, 133, 83, 221, 197, 195, 207, 30, 196, 26, 122, 186, 45, 90, 27, 145, 130, 62, 159, 10, 18, 250, 179, 135, 90, 159, 56, 160, 141, 78, 234, 181, 54, 177, 30, 122, 102, 236, 57, 143, 72, 148, 169, 129, 165, 114, 216, 73, 88, 62, 207, 82, 87, 241, 130, 203, 224, 142, 244, 149, 108, 61, 147, 60, 45, 129, 140, 184, 248, 87, 128, 163, 202, 235, 5, 215, 128, 67, 251, 220, 80, 196, 27, 25, 114, 119, 216, 197, 29, 212, 42, 18, 143, 89, 7, 247, 195, 83, 206, 210, 81, 214, 42, 59, 42, 130, 98, 192, 168, 195, 208, 68, 155, 172, 68, 69, 214, 85, 223, 245, 217, 63, 159, 119, 114, 250, 162, 118, 227, 52, 135, 81, 254, 127, 79, 91, 236, 233, 41, 194, 143, 216, 26, 116, 33, 163, 141, 4, 218, 81, 169, 168, 223, 212, 84, 75, 241, 47, 148, 84, 170, 192, 125, 251, 4, 131, 113, 185, 170, 168, 243, 250, 238, 179, 55, 77, 112, 231, 69, 109, 232, 134, 1, 33, 46, 205, 222, 34, 75, 161, 47, 132, 19, 253, 16, 76, 213, 108, 218, 211, 25, 136, 167, 246, 153, 246, 40, 30, 238, 57, 153, 123, 156, 86, 131, 132, 40, 220, 143, 242, 217, 198, 197, 236, 155, 15, 142, 157, 24, 204, 2, 222, 185, 172, 224, 226, 91, 159, 15, 16, 197, 136, 229, 78, 78, 198, 54, 7, 255, 197, 179, 160, 176, 177, 50, 55, 28, 32, 203, 232, 251, 177, 129, 92, 29, 185, 90, 69, 125, 146, 205, 109, 51, 35, 75, 233, 189, 10, 133, 177, 73, 14, 65, 151, 56, 152, 214, 74, 139, 135, 139, 17, 108, 66, 201, 87, 117, 52, 37, 76, 247, 158, 129, 84, 28, 248, 46, 32, 115, 4, 94, 0, 244, 14, 245, 68, 38, 142, 6, 47, 6, 224, 158, 18, 95, 142, 250, 64, 229, 178, 176, 251, 50, 182, 245, 206, 90, 184, 111, 198, 240, 81, 177, 44, 78, 169, 175, 170, 135, 207, 192, 171, 199, 228, 164, 111, 18, 53, 253, 123, 38, 44, 175, 190, 78, 113, 65, 146, 97, 180, 141, 90, 160, 227, 222, 46, 156, 64, 42, 104, 216, 208, 114, 2, 181, 123, 194, 176, 188, 122, 235, 223, 224, 74, 157, 182, 84, 172, 150, 234, 99, 166, 39, 103, 242, 247, 219, 75, 223, 92, 154, 54, 80, 238, 226, 18, 17, 81, 108, 162, 87, 51, 128, 239, 96, 174, 56, 51, 122, 130, 143, 178, 140, 188, 153, 141, 120, 4, 183, 54, 42, 87, 158, 20, 182, 136, 248, 40, 154, 108, 116, 143, 167, 140, 77, 136, 212, 23, 29, 218, 246, 248, 129, 103, 33, 99, 195, 33, 13, 56, 64, 230, 120, 141, 99, 102, 102, 37, 236, 166, 214, 165, 235, 52, 104, 22, 229, 182, 167, 107, 251, 158, 192, 184, 7, 13, 245, 144, 39, 201, 228, 99, 92, 49, 182, 183, 195, 101, 35, 221, 59, 253, 174, 138, 130, 87, 167, 231, 233, 117, 244, 39, 187, 98, 254, 13, 210, 71, 236, 231, 166, 108, 172, 157, 216, 176, 180, 210, 181, 195, 86, 135, 86, 73, 56, 249, 192, 135, 242, 237, 216, 74, 173, 153, 12, 204, 187, 143, 83, 118, 243, 110, 115, 190, 151, 193, 229, 2, 88, 190, 87, 81, 35, 14, 161, 112, 126, 69, 134, 55, 18, 201, 217, 9, 189, 223, 205, 149, 231, 44, 132, 24, 78, 1, 191, 34, 43, 122, 200, 83, 124, 98, 99, 21, 118, 217, 215, 181, 129, 38, 177, 231, 251, 206, 149, 119, 177, 246, 198, 95, 50, 131, 253, 147, 97, 75, 132, 96, 64, 88, 182, 56, 193, 142, 147, 198, 62, 190, 157, 230, 229, 35, 193, 150, 46, 88, 250, 176, 190, 152, 239, 129, 162, 208, 38, 204, 42, 11, 118, 69, 82, 159, 60, 192, 82, 69, 57, 81, 45, 63, 106, 129, 200, 70, 234, 232, 252, 230, 87, 122, 17, 83, 147, 7, 255, 195, 30, 22, 246, 238, 139, 155, 59, 219, 75, 176, 174, 135, 176, 86, 12, 6, 161, 96, 112, 119, 226, 90, 144, 38, 248, 39, 170, 9, 183, 76, 227, 22, 162, 241, 140, 31, 62, 134, 157, 28, 72, 154, 128, 47, 30, 93, 184, 234, 163, 117, 10, 47, 30, 174, 53, 247, 20, 142, 66, 87, 22, 152, 208, 79, 240, 92, 45, 156, 43, 175, 81, 122, 48, 155, 190, 155, 25, 188, 231, 135, 192, 234, 227, 237, 240, 41, 125, 28, 209, 125, 74, 76, 241, 81, 212, 255, 255, 20, 68, 137, 24, 100, 128, 25, 50, 180, 168, 154, 114, 205, 45, 3, 174, 21, 4, 220, 135, 233, 62, 215, 141, 123, 223, 19, 125, 9, 143, 19, 60, 98, 183, 219, 95, 45, 234, 188, 253, 112, 172, 187, 240, 193, 124, 201, 174, 21, 55, 220, 35, 113, 245, 76, 160, 174, 56, 255, 2, 242, 69, 206, 165, 79, 245, 98, 66, 100, 92, 20, 140, 133, 37, 152, 221, 124, 43, 242, 116, 153, 82, 21, 120, 145, 106, 218, 47, 131, 244, 98, 18, 177, 186, 102, 221, 164, 13, 220, 210, 251, 199, 183, 204, 93, 203, 122] }) }, ref udp = UdpHeader { source_port: 37417, destination_port: 9108, length: 55031, checksum: 30539 }, ref tcp = TcpHeader { source_port: 890, destination_port: 63900, sequence_number: 1007304455, acknowledgment_number: 4179598739, ns: false, fin: false, syn: false, rst: true, psh: true, ack: true, urg: true, ece: false, cwr: false, window_size: 26948, checksum: 44775, urgent_pointer: 49253, options: [Err(UnknownId(91))] }, ref icmpv4 = Icmpv4Header { icmp_type: Unknown { type_u8: 115, code_u8: 63, bytes5to8: [197, 108, 248, 120] }, checksum: 2225 }, ref icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 78, code_u8: 128, bytes5to8: [71, 156, 20, 196] }, checksum: 11773 }, ref payload = [167, 22, 235, 99, 149, 6, 145, 225, 240, 172, 90, 57, 224, 202, 18, 160, 59, 126, 6, 142, 246, 221, 178, 142, 119, 149, 118, 24, 85, 202, 49, 206, 46, 138, 89, 120, 205, 44, 146, 68, 109, 183, 62, 194, 214, 224, 242, 51, 244, 121, 41, 30, 100, 88, 113, 252, 247, 30, 5, 51, 65, 174, 180, 40, 186, 204, 107, 12, 94, 171, 77, 238, 219, 1, 48, 192, 233, 160, 118, 122, 231, 175, 205, 222, 71, 3, 152, 89, 179, 124, 235, 147, 214, 240, 151, 170, 249, 41, 219, 41, 174, 39, 72, 81, 93, 33, 92, 38, 81, 230, 203, 184, 62, 40, 164, 56, 17, 246, 208, 119, 53, 119, 56, 57, 208, 123, 242, 126, 113, 173, 155, 254, 34, 97, 224, 150, 30, 183, 26, 88, 233, 142, 107, 189, 73, 181, 188, 88, 191, 151, 196, 115, 94, 128, 216, 121, 206, 65, 184, 150, 7, 85, 157, 169, 48, 0, 91, 59, 140, 20, 61, 231, 19, 43, 25, 8, 45, 153, 139, 171, 203, 72, 129, 96, 125, 200, 107, 24, 66, 116, 196, 166, 70, 84, 100, 252, 148, 198, 140, 190, 179, 227, 171, 217, 73, 201, 196, 252, 103, 198, 139, 22, 44, 141, 44, 49, 115, 210, 114, 152, 85, 78, 192, 14, 153, 14, 216, 154, 194, 198, 148, 217, 216, 28, 163, 245, 132, 125, 198, 159, 156, 92, 241, 20, 198, 42, 79, 230, 130, 38, 126, 161, 11, 92, 79, 203, 0, 104, 164, 106, 146, 111, 205, 110, 59, 18, 211, 10, 1, 215, 152, 28, 161, 31, 213, 126, 52, 199, 37, 225, 26, 181, 172, 222, 157, 154, 89, 215, 89, 91, 22, 86, 209, 30, 49, 149, 245, 117, 42, 128, 12, 38, 228, 11, 123, 4, 20, 233, 167, 243, 162, 158, 85, 20, 26, 13, 56, 54, 140, 115, 124, 233, 205, 48, 216, 209, 24, 245, 201, 81, 124, 205, 226, 125, 168, 248, 160, 149, 105, 214, 105, 200, 88, 229, 231, 148, 61, 84, 108, 111, 227, 248, 41, 155, 141, 56, 50, 10, 62, 239, 227, 153, 54, 253, 167, 244, 2, 96, 251, 214, 151, 51, 26, 240, 151, 251, 49, 68, 199, 115, 158, 72, 220, 201, 221, 238, 235, 163, 62, 221, 143, 228, 206, 176, 232, 134, 88, 55, 239, 9, 16, 20, 170, 132, 185, 192, 183, 227, 25, 192, 190, 109, 197, 130, 244, 221, 246, 129, 185, 45, 83, 250, 230, 19, 17, 167, 53, 2, 58, 206, 250, 223, 172, 35, 161, 236, 241, 62, 48, 120, 252, 211, 64, 57, 197, 163, 207, 224, 97, 67, 98, 225, 232, 193, 3, 173, 63, 83, 208, 180, 85, 133, 217, 177, 127, 4, 83, 113, 190, 79, 44, 36, 135, 204, 213, 181, 92, 154, 81, 8, 96, 191, 91, 5, 156, 218, 86, 145, 145, 223, 3, 111, 113, 118, 201, 40, 188, 31, 173, 75, 75, 239, 52, 253, 125, 88, 33, 169, 110, 187, 46, 103, 172, 70, 65, 160, 214, 149, 119, 36, 247, 215, 247, 91, 174, 111, 33, 180, 116, 196, 18, 142, 170, 40, 254, 155, 252, 96, 233, 59, 227, 218, 60, 219, 50, 183, 191, 19, 217, 175, 91, 226, 201, 246, 119, 151, 30, 179, 161, 243, 140, 221, 241, 144, 7, 80, 218, 64, 130, 44, 149, 253, 30, 64, 102, 58, 7, 184, 195, 16, 22, 127, 220, 129, 169, 45, 227, 162, 222, 144, 129, 10, 214, 191, 198, 12, 226, 227, 96, 96, 206, 101, 42, 110, 187, 140, 235, 160, 180, 201, 233, 146, 73, 67, 88, 81, 159, 116, 121, 73, 152, 161, 155, 241, 104, 71, 192, 252, 199, 162, 135, 203, 243, 38, 156, 198, 220, 13, 77, 57, 78, 40, 30, 127, 227, 104, 95, 81, 58, 156, 141, 193, 142, 36, 243, 57, 42, 43, 255, 72, 3, 101, 2, 124, 126, 77, 225, 190, 211, 134, 82, 180, 50, 0, 160, 155, 232, 219, 242, 36, 57, 46, 165, 60, 68, 123, 196, 144, 153, 209, 191, 62, 224, 188, 250, 211, 179, 157, 71, 164, 218, 132, 202, 63, 21, 83, 68, 132, 213, 204, 189, 169, 126, 242, 176, 93, 211, 209, 164, 134, 153, 161, 22, 134, 197, 184, 105, 191, 186, 152, 145, 133, 119, 183, 65, 55, 248, 204, 31, 28, 172, 85, 228, 86, 235, 47, 63, 235, 254, 93, 132, 86, 70, 233, 216, 2, 63, 26, 82, 45, 10, 182, 171, 13, 142, 142, 154, 50, 15, 133, 65, 137, 197, 3, 209, 20, 126, 93, 195, 249, 135, 44, 114, 55, 115, 33, 231, 195, 252, 42, 143, 181, 34, 52, 2, 183, 132, 124, 255, 83, 13, 19, 99, 236, 138, 64, 54, 176, 106, 29, 46, 222, 141, 142, 26, 167, 207, 104, 82, 178, 150, 26, 2, 59, 105, 37, 10, 229, 93, 30, 46, 204, 55, 56, 4, 29, 8, 212, 178, 77, 221, 53, 222, 191, 172, 85, 127, 159, 44, 73, 37, 251, 153, 173, 134, 13, 224, 164, 23, 35, 153, 190, 22, 22, 52, 28, 6, 28, 112, 117, 123, 216, 141, 98, 47, 140, 113, 1, 84, 236, 144, 94, 7, 71, 16, 196, 144, 96, 129, 29, 209, 66, 237, 67, 224, 10, 16, 160, 244, 217, 145, 241, 58, 83, 30, 77, 179, 140, 124, 243, 97, 121, 10, 54, 1, 30, 11, 116, 127, 188, 15, 122, 29, 133, 100, 178, 110, 201, 188, 63, 216, 168, 30, 6, 55, 127, 57, 255, 158, 222, 190, 110, 23, 36, 64, 230, 20, 155, 114, 16, 4, 60, 7, 71, 18, 44, 110, 66, 47, 217, 153, 202, 72, 66, 158, 204, 97, 44, 107, 172] +cc dc17582d10107fd94fddbed59a7cf5c4208a8b26c523d0cc916aee7e88bec69c # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0x0000 }, ref vlan0 = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan1 = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan2 = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref macsec0 = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ref macsec1 = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ref macsec2 = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ref ipv4 = Ipv4Header { dscp: Ipv4Dscp(42), ecn: Ipv4Ecn(1), total_len: 52204, identification: 44039, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(4556), time_to_live: 151, protocol: 221, header_checksum: 30718, source: [7, 225, 117, 228], destination: [23, 66, 165, 207], options: [] }, ref ipv4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 232, spi: 493231867, sequence_number: 3478019815, raw_icv: [85, 93, 40, 183, 94, 65, 80, 220, 128, 79, 196, 241, 63, 88, 131, 191, 146, 253, 53, 179, 71, 188, 51, 177, 27, 57, 41, 215, 66, 34, 251, 202, 162, 238, 250, 174, 198, 118, 130, 49, 97, 178, 170, 199, 100, 17, 54, 20, 237, 1, 212, 38, 237, 230, 154, 201, 135, 82, 210, 123, 101, 235, 35, 37, 223, 40, 127, 157, 246, 113, 60, 37, 80, 253, 134, 228, 72, 165, 149, 169, 216, 124, 247, 31, 208, 99, 21, 167, 183, 94, 139, 33, 179, 184, 146, 194, 58, 81, 124, 96, 163, 35, 22, 9, 54, 128, 27, 68, 123, 104, 139, 71, 174, 206, 216, 15, 74, 51, 113, 181, 79, 77, 104, 80, 89, 144, 162, 201, 111, 61, 217, 189, 60, 217, 114, 2, 234, 116, 144, 20, 220, 171, 64, 134, 79, 65, 19, 85, 253, 114, 29, 19, 137, 40, 24, 61, 230, 32, 100, 52, 114, 243, 159, 140, 38, 71, 217, 181, 108, 82, 93, 36, 254, 120, 22, 89, 65, 18, 199, 155, 150, 181, 175, 166, 180, 91, 249, 144, 207, 94, 227, 51, 138, 26, 216, 237, 157, 57, 165, 248, 76, 248, 29, 52, 138, 133, 108, 209, 223, 234, 185, 5, 61, 208, 64, 252, 152, 220, 75, 56, 103, 129, 198, 204, 222, 250, 195, 19, 68, 103, 32, 204, 235, 78, 30, 23, 0, 30, 42, 0, 109, 206, 97, 106, 35, 6, 52, 66, 51, 223, 127, 152, 18, 164, 123, 255, 248, 22, 18, 176, 71, 93, 250, 126, 103, 50, 21, 53, 255, 10, 63, 201, 18, 8, 214, 0, 93, 251, 198, 51, 74, 154, 132, 88, 61, 225, 93, 14, 144, 15, 165, 160, 87, 108, 232, 11, 165, 252, 125, 118, 91, 153, 3, 221, 169, 75, 206, 145, 66, 20, 163, 46, 134, 196, 133, 115, 112, 180, 84, 33, 253, 249, 154, 78, 158, 95, 149, 113, 0, 248, 80, 82, 236, 242, 193, 33, 104, 65, 90, 115, 24, 195, 144, 128, 161, 71, 201, 31, 167, 24, 40, 43, 187, 2, 3, 141, 215, 210, 5, 140, 25, 60, 27, 85, 105, 132, 78, 211, 155, 79, 74, 79, 176, 46, 44, 111, 137, 153, 131, 195, 111, 101, 132, 183, 117, 168, 54, 212, 180, 249, 140, 101, 67, 124, 218, 142, 167, 167, 0, 97, 242, 91, 118, 186, 25, 223, 178, 228, 203, 155, 190, 5, 222, 142, 122, 207, 35, 125, 107, 171, 172, 52, 141, 223, 187, 65, 100, 65, 145, 128, 29, 90, 155, 165, 60, 239, 191, 225, 165, 58, 73, 247, 96, 8, 117, 12, 235, 18, 43, 29, 196, 183, 179, 136, 207, 38, 13, 87, 52, 103, 140, 0, 65, 140, 135, 130, 121, 41, 48, 200, 230, 172, 127, 31, 67, 160, 27, 13, 224, 1, 120, 149, 34, 86, 69, 124, 222, 26, 210, 234, 104, 42, 123, 183, 239, 111, 153, 192, 206, 207, 143, 15, 121, 23, 202, 92, 197, 182, 193, 65, 225, 193, 128, 46, 174, 253, 154, 153, 37, 246, 164, 177, 31, 40, 65, 143, 55, 239, 105, 53, 141, 174, 198, 22, 77, 133, 81, 243, 16, 135, 170, 109, 166, 162, 57, 186, 225, 61, 165, 153, 200, 101, 12, 27, 1, 45, 47, 65, 20, 27, 16, 249, 203, 206, 70, 234, 91, 104, 135, 218, 40, 18, 46, 36, 214, 219, 101, 118, 95, 195, 33, 28, 69, 22, 127, 193, 226, 179, 202, 234, 21, 91, 28, 121, 81, 5, 47, 61, 208, 151, 143, 213, 94, 215, 115, 214, 175, 58, 70, 47, 89, 206, 96, 253, 183, 45, 161, 54, 104, 247, 155, 25, 153, 84, 125, 251, 128, 76, 225, 238, 247, 244, 140, 232, 34, 253, 12, 121, 207, 105, 206, 197, 105, 177, 2, 107, 181, 86, 75, 193, 245, 162, 246, 7, 32, 98, 224, 173, 219, 234, 4, 11, 235, 48, 7, 118, 57, 251, 234, 129, 112, 174, 34, 81, 177, 31, 203, 137, 254, 138, 26, 112, 239, 230, 131, 39, 82, 180, 159, 87, 84, 25, 69, 16, 44, 56, 65, 157, 67, 55, 191, 226, 5, 67, 57, 231, 94, 100, 249, 189, 209, 145, 189, 125, 78, 55, 104, 51, 77, 32, 189, 216, 250, 49, 237, 34, 13, 137, 237, 34, 48, 109, 147, 92, 62, 245, 95, 130, 56, 252, 38, 148, 144, 157, 89, 201, 215, 104, 144, 145, 244, 87, 156, 253, 61, 26, 85, 3, 190, 223, 104, 197, 98, 107, 80, 199, 24, 238] }) }, ref ipv6 = Ipv6Header { traffic_class: 5, flow_label: Ipv6FlowLabel(538693), payload_length: 46308, next_header: 234, hop_limit: 55, source: [146, 121, 12, 236, 217, 249, 88, 148, 98, 204, 179, 5, 182, 122, 210, 132], destination: [227, 105, 167, 21, 130, 114, 118, 136, 228, 155, 198, 110, 133, 134, 100, 229] }, ref ipv6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: None, auth: Some(IpAuthHeader { next_header: 206, spi: 1845380884, sequence_number: 3657579772, raw_icv: [137, 176, 140, 96, 42, 56, 82, 192, 212, 242, 182, 204, 182, 77, 134, 37, 163, 235, 161, 31, 37, 243, 77, 11, 86, 153, 172, 193, 33, 168, 126, 28, 208, 109, 115, 0, 243, 80, 211, 245, 142, 177, 139, 28, 156, 66, 38, 184, 116, 66, 2, 52, 205, 68, 104, 118, 80, 117, 112, 138, 42, 176, 25, 207, 35, 231, 46, 138, 193, 202, 229, 42, 226, 230, 100, 125, 127, 133, 111, 251, 185, 182, 238, 225, 185, 20, 80, 235, 215, 124, 180, 208, 103, 225, 164, 197, 85, 238, 178, 17, 231, 187, 134, 114, 67, 207, 189, 243, 196, 246, 105, 72, 187, 117, 14, 9, 155, 224, 221, 193, 246, 152, 90, 10, 100, 36, 181, 39, 61, 225, 210, 83, 52, 122, 207, 249, 248, 153, 0, 214, 32, 42, 234, 83, 117, 138, 0, 138, 77, 203, 1, 52, 120, 3, 217, 227, 239, 120, 241, 85, 81, 7, 118, 51, 3, 249, 139, 33, 33, 227, 120, 198, 0, 147, 247, 3, 84, 127, 77, 248, 236, 135, 201, 28, 183, 194, 48, 36, 45, 168, 225, 62, 181, 41, 20, 220, 174, 191, 50, 102, 219, 47, 220, 198, 42, 73, 174, 72, 129, 253, 125, 34, 142, 39, 190, 57, 154, 64, 62, 68, 125, 9, 2, 79, 239, 236, 58, 165, 62, 99, 56, 172, 236, 72, 147, 216, 34, 164, 218, 212, 174, 33, 209, 38, 67, 138, 214, 97, 220, 229, 40, 95, 34, 197, 91, 4, 175, 102, 76, 145, 86, 26, 31, 232, 179, 137, 51, 66, 202, 175, 42, 50, 137, 79, 119, 18, 130, 87, 132, 183, 141, 87, 220, 50, 10, 156, 54, 126, 59, 72, 88, 191, 108, 208, 41, 69, 215, 89, 222, 253, 52, 114, 12, 224, 59, 224, 92, 72, 124, 253, 51, 62, 3, 57, 3, 145, 20, 94, 215, 178, 116, 83, 19, 5, 134, 206, 226, 68, 224, 65, 12, 52, 167, 161, 38, 232, 86, 29, 165, 52, 148, 135, 54, 245, 212, 83, 178, 152, 157, 29, 193, 150, 95, 2, 188, 249, 4, 200, 144, 115, 203, 200, 108, 185, 117, 79, 123, 24, 114, 246, 215, 107, 84, 98, 96, 102, 193, 9, 12, 119, 206, 44, 66, 249, 223, 153, 193, 162, 199, 5, 146, 214, 75, 108, 32, 70, 185, 91, 56, 131, 97, 123, 10, 99, 230, 108, 164, 136, 107, 251, 199, 219, 115, 215, 140, 10, 129, 229, 182, 21, 8, 186, 255, 105, 152, 191, 49, 243, 239, 62, 3, 207, 68, 252, 158, 34, 130, 199, 104, 166, 74, 253, 197, 52, 6, 218, 137, 130, 173, 3, 241, 134, 168, 92, 163, 9, 175, 125, 201, 84, 58, 183, 109, 21, 225, 107, 109, 137, 52, 162, 201, 165, 241, 234, 39, 193, 113, 173, 117, 47, 0, 130, 98, 141, 23, 79, 12, 94, 147, 101, 32, 3, 95, 224, 154, 31, 91, 205, 250, 152, 186, 196, 130, 67, 180, 98, 248, 193, 205, 245, 49, 18, 208, 43, 139, 209, 168, 89, 244, 109, 27, 236, 30, 132, 230, 47, 107, 97, 209, 44, 145, 143, 1, 84, 182, 108, 250, 47, 136, 124, 205, 115, 79, 94, 96, 14, 65, 248, 24, 228, 30, 236, 17, 152, 159, 122, 214, 65, 28, 169, 151, 30, 74, 198, 122, 148, 181, 216, 160, 96, 51, 182, 157, 129, 212, 201, 64, 103, 236, 29, 130, 166, 207, 59, 148, 200, 143, 19, 5, 219, 52, 61, 228, 103, 2, 13, 221, 101, 130, 226, 147, 48, 141, 68, 177, 18, 52, 249, 133, 205, 47, 65, 122, 187, 224, 30, 204, 55, 73, 248, 199, 118, 255, 32, 173, 49, 178, 254, 138, 57, 195, 11, 58, 144, 215, 14, 197, 242, 62, 174, 86, 25, 38, 208, 118, 145, 235, 70, 26, 197, 39, 150, 24, 217, 74, 190, 243, 90, 5, 203, 133, 206, 130, 3, 42, 161, 111, 196, 63, 185, 22, 244, 104, 89, 117, 137, 102, 59, 155, 63, 8, 231, 253, 215, 99, 153, 213, 114, 19, 45, 81, 87, 238, 226, 216, 69, 213, 39, 93, 4, 94, 41, 172, 169, 46, 133, 176, 36, 202, 97, 57, 181, 101, 163, 72, 22, 239, 63, 220, 140, 81, 28, 77, 127, 76, 121, 139, 162, 94, 204, 196, 181, 152, 153, 237, 164, 177, 68, 102, 218, 114, 252, 223, 90, 190, 29, 92, 63, 45, 146, 89, 223, 114, 241, 180, 41, 225, 85, 134, 76, 109, 122, 242, 187, 201, 194, 74, 92, 55, 246, 192, 214] }) }, ref arp = ArpPacket { hw_addr_type: 0x0F46, proto_addr_type: 0x94F6, hw_addr_size: 134, proto_addr_size: 175, operation: ArpOperation(27167), sender_hw_addr: [1, 37, 42, 81, 80, 13, 11, 254, 107, 219, 175, 166, 244, 215, 252, 142, 129, 157, 212, 49, 164, 106, 165, 202, 84, 19, 240, 51, 96, 209, 73, 63, 156, 82, 61, 55, 120, 178, 55, 137, 10, 28, 166, 199, 141, 199, 58, 43, 120, 91, 219, 190, 237, 27, 161, 219, 173, 150, 213, 107, 76, 175, 225, 136, 56, 87, 46, 74, 27, 146, 166, 108, 88, 23, 49, 111, 188, 165, 25, 159, 101, 62, 139, 235, 130, 55, 244, 176, 178, 148, 171, 107, 61, 207, 72, 203, 178, 138, 85, 140, 216, 35, 188, 18, 26, 241, 131, 145, 73, 21, 168, 43, 198, 52, 37, 136, 79, 236, 66, 168, 88, 254, 185, 45, 195, 79, 189, 43, 42, 242, 248, 30, 201, 162], sender_protocol_addr: [40, 164, 92, 139, 53, 167, 187, 166, 110, 103, 56, 197, 44, 19, 237, 145, 63, 58, 182, 31, 79, 19, 153, 240, 248, 140, 83, 114, 163, 114, 25, 188, 222, 36, 84, 171, 104, 38, 102, 54, 157, 91, 39, 80, 248, 130, 75, 156, 147, 134, 44, 152, 216, 71, 107, 245, 223, 243, 113, 10, 108, 234, 195, 13, 187, 57, 84, 2, 175, 52, 209, 136, 192, 81, 253, 251, 90, 131, 178, 141, 177, 15, 148, 250, 149, 83, 24, 183, 255, 58, 231, 139, 153, 199, 85, 8, 33, 20, 140, 42, 221, 224, 174, 255, 213, 206, 18, 121, 2, 208, 20, 87, 226, 94, 144, 141, 97, 111, 68, 174, 76, 134, 138, 25, 128, 203, 30, 217, 157, 74, 181, 168, 115, 72, 174, 27, 78, 255, 54, 75, 135, 86, 92, 79, 215, 125, 222, 58, 58, 244, 216, 222, 45, 73, 162, 182, 136, 143, 64, 190, 96, 169, 10, 247, 180, 47, 73, 172, 237, 152, 232, 224, 205, 112, 49], target_hw_addr: [64, 93, 48, 199, 200, 118, 117, 109, 222, 199, 135, 210, 166, 48, 68, 119, 226, 83, 91, 128, 42, 58, 98, 4, 191, 41, 166, 227, 165, 160, 55, 238, 212, 190, 191, 46, 167, 78, 127, 135, 171, 191, 241, 98, 181, 40, 55, 64, 158, 82, 83, 106, 68, 93, 103, 228, 63, 126, 29, 52, 139, 209, 219, 123, 248, 106, 2, 123, 136, 105, 43, 245, 121, 80, 178, 96, 238, 21, 236, 205, 157, 226, 249, 174, 165, 148, 221, 246, 41, 132, 67, 192, 106, 23, 74, 73, 226, 62, 133, 163, 7, 94, 106, 152, 240, 6, 70, 10, 234, 58, 148, 89, 137, 84, 234, 17, 213, 86, 255, 81, 89, 168, 234, 133, 34, 10, 192, 121, 65, 188, 37, 54, 43, 147], target_protocol_addr: [65, 98, 185, 60, 223, 164, 70, 145, 101, 2, 85, 93, 120, 16, 153, 254, 41, 30, 58, 137, 170, 241, 248, 114, 195, 223, 30, 72, 18, 126, 73, 167, 226, 179, 27, 133, 48, 2, 181, 123, 221, 102, 192, 90, 61, 79, 203, 58, 155, 2, 66, 5, 98, 167, 11, 95, 13, 127, 83, 250, 129, 152, 234, 136, 2, 14, 245, 71, 124, 8, 146, 146, 67, 138, 213, 211, 181, 226, 36, 2, 69, 117, 186, 244, 29, 134, 179, 209, 254, 12, 171, 102, 221, 200, 114, 65, 241, 222, 117, 95, 179, 33, 18, 183, 223, 54, 252, 159, 0, 21, 196, 167, 147, 214, 233, 223, 188, 173, 152, 101, 138, 204, 128, 48, 140, 172, 101, 20, 64, 167, 154, 28, 158, 196, 129, 204, 61, 179, 186, 163, 21, 202, 179, 19, 135, 52, 43, 86, 101, 31, 76, 234, 49, 80, 101, 117, 60, 134, 108, 182, 150, 104, 55, 115, 207, 165, 215, 37, 20, 98, 106, 31, 80, 50, 222] }, ref udp = UdpHeader { source_port: 52220, destination_port: 51065, length: 11126, checksum: 17171 }, ref tcp = TcpHeader { source_port: 25330, destination_port: 52834, sequence_number: 1922811557, acknowledgment_number: 248581598, ns: false, fin: false, syn: true, rst: true, psh: true, ack: false, urg: true, ece: true, cwr: false, window_size: 44125, checksum: 60648, urgent_pointer: 14489, options: [Err(UnknownId(58))] }, ref icmpv4 = Icmpv4Header { icmp_type: Unknown { type_u8: 232, code_u8: 156, bytes5to8: [212, 237, 132, 212] }, checksum: 45828 }, ref icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 65, code_u8: 55, bytes5to8: [133, 7, 13, 249] }, checksum: 3792 }, ref payload = [103, 132, 44, 211, 53, 235, 103, 88, 148, 31, 116, 218, 219, 23, 4, 140, 221, 160] diff --git a/etherparse/proptest-regressions/link/lax_link_ext_slice.txt b/etherparse/proptest-regressions/link/lax_link_ext_slice.txt new file mode 100644 index 00000000..b56c9668 --- /dev/null +++ b/etherparse/proptest-regressions/link/lax_link_ext_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc d6c35d4295255dbddecc82e6fa137d7a138d869cdade93165b972bd23217edfa # shrinks to vlan = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, macsec = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ethertype = 0x0000 diff --git a/etherparse/proptest-regressions/link/lax_macsec_slice.txt b/etherparse/proptest-regressions/link/lax_macsec_slice.txt new file mode 100644 index 00000000..2e4f9036 --- /dev/null +++ b/etherparse/proptest-regressions/link/lax_macsec_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 445d2cb2bb5b24a1094bb52f109956b7608ba2910bdd11bb8e32115976398660 # shrinks to macsec = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None } diff --git a/etherparse/proptest-regressions/link/link_ext_slice.txt b/etherparse/proptest-regressions/link/link_ext_slice.txt new file mode 100644 index 00000000..b517cf81 --- /dev/null +++ b/etherparse/proptest-regressions/link/link_ext_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc b9680cab0923704b74563ac762bf321510faf67286af3ba221f6ca1537dd69b9 # shrinks to vlan = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, macsec = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: Some(0) }, ether_type = 0x0000 diff --git a/etherparse/proptest-regressions/link/linux_sll_header_slice.txt b/etherparse/proptest-regressions/link/linux_sll_header_slice.txt new file mode 100644 index 00000000..52f26d1c --- /dev/null +++ b/etherparse/proptest-regressions/link/linux_sll_header_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc e14e4c7280be51164b675912e97895f4919b07792e7fd41ab8c9bad5356465e1 # shrinks to input = LinuxSllHeader { packet_type: 0 (Sent to us), arp_hrd_type: 824 (Netlink header), sender_address_valid_length: 0, sender_address: [0, 0, 0, 0, 0, 0, 0, 0], protocol_type: NetlinkProtocolType(0) } diff --git a/etherparse/proptest-regressions/link/mac_sec_an.txt b/etherparse/proptest-regressions/link/mac_sec_an.txt new file mode 100644 index 00000000..17267913 --- /dev/null +++ b/etherparse/proptest-regressions/link/mac_sec_an.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc bede3366c70950fac8a449294adc500d9f5ee5c956c66896ab6ea14c1b0bef45 # shrinks to valid_value = 4 diff --git a/etherparse/proptest-regressions/link/mac_sec_header.txt b/etherparse/proptest-regressions/link/mac_sec_header.txt new file mode 100644 index 00000000..f9db4554 --- /dev/null +++ b/etherparse/proptest-regressions/link/mac_sec_header.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 66ab8c1dc7f1c333bd90c5289bdef647537372b1b9ce894d34b3a23422abc54f # shrinks to header = MacSecHeader { ptype: Unmodified(0x0001), endstation_id: false, scb: false, an: MacSecAn(0), short_len: MacSecShortLen(0), packet_nr: 0, sci: None } +cc 604f124b27176f7ff3c97f86d16e36928f51f7daa79c30eea702e5248f9f2abe # shrinks to header = MacSecHeader { ptype: Unmodified(0x0001), endstation_id: false, scb: false, an: MacSecAn(0), short_len: MacSecShortLen(0), packet_nr: 0, sci: None } diff --git a/etherparse/proptest-regressions/link/mac_sec_sl.txt b/etherparse/proptest-regressions/link/mac_sec_sl.txt new file mode 100644 index 00000000..3aca73e6 --- /dev/null +++ b/etherparse/proptest-regressions/link/mac_sec_sl.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc f970f91fec10c3eed54d6eb268b9e5cf6e32e19a5dff1586bf78ca99362a7b23 # shrinks to valid_value = 0, invalid_value = 8 +cc 614402f8cc83702195d27bfee2bc8b56aaf904f8dd858773cb9f9d6c093d426a # shrinks to valid_value = 0, invalid_value = 8 diff --git a/etherparse/proptest-regressions/link/macsec_header.txt b/etherparse/proptest-regressions/link/macsec_header.txt new file mode 100644 index 00000000..5aa5aec7 --- /dev/null +++ b/etherparse/proptest-regressions/link/macsec_header.txt @@ -0,0 +1,9 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc d15fc6bd85097c58f9ba998fc10940a19ab7307798cb469b426460228be06ab2 # shrinks to header = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ether_type = 0x0000, valid_unmodified_len = 2, invalid_unmodified_len_0 = 0, invalid_unmodified_len_1 = 64, valid_modified_len = 1, invalid_modified_len = 64 +cc 5c365eb47d1c5896957cd70b55708aab545924c84dba5de6d1d8ae7855a850f4 # shrinks to header = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(1), packet_nr: 0, sci: None } +cc f5537f4646b939dfa8168d93a1be25c58d326acda8604af3ae382fbf14fd0a2f # shrinks to header = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ether_type = 0x0000, valid_unmodified_len = 62, invalid_unmodified_len = 64, valid_modified_len = 1, invalid_modified_len = 64 diff --git a/etherparse/proptest-regressions/link/macsec_header_slice.txt b/etherparse/proptest-regressions/link/macsec_header_slice.txt new file mode 100644 index 00000000..22c8484c --- /dev/null +++ b/etherparse/proptest-regressions/link/macsec_header_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 4a82fa88308d823cc3bb0d7da35d367c7475990dc8daaa4c1a5455445ccd41ff # shrinks to macsec = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ethertype = 0x0000, sci = 0 diff --git a/etherparse/proptest-regressions/link/macsec_slice.txt b/etherparse/proptest-regressions/link/macsec_slice.txt new file mode 100644 index 00000000..b03097d6 --- /dev/null +++ b/etherparse/proptest-regressions/link/macsec_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 32e7fe8e0e32be55152e2d893aff9099856ba4f19b1babb9a767fb4b44979167 # shrinks to macsec = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ethertype = 0x0000, non_zero_sl = 1 diff --git a/etherparse/proptest-regressions/link/vlan_slice.txt b/etherparse/proptest-regressions/link/vlan_slice.txt new file mode 100644 index 00000000..446a3555 --- /dev/null +++ b/etherparse/proptest-regressions/link/vlan_slice.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc dadc8babb24c7b9b8eaf2e0c4e75312dd9704c267a33c8a68906ccec14fa8b92 # shrinks to single = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, double = DoubleVlanHeader { outer: SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(61), ether_type: 0x88A8 (IEEE Std 802.1Q - Service VLAN tag identifier (S-Tag)) }, inner: SingleVlanHeader { pcp: VlanPcp(2), drop_eligible_indicator: true, vlan_id: VlanId(472), ether_type: 0x0001 } } diff --git a/etherparse/proptest-regressions/sliced_packet.txt b/etherparse/proptest-regressions/sliced_packet.txt index c09057b8..899ce7be 100644 --- a/etherparse/proptest-regressions/sliced_packet.txt +++ b/etherparse/proptest-regressions/sliced_packet.txt @@ -5,3 +5,5 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc b196b71d1fd361e0dc02078dcedaa8e67bb496f2cb76ceddd1fa51ce90c70f6d # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0x0000 }, ref linux_sll = LinuxSllHeader { packet_type: 0 (Sent to us), arp_hrd_type: 824 (Netlink header), sender_address_valid_length: 0, sender_address: [0, 0, 0, 0, 0, 0, 0, 0], protocol_type: NetlinkProtocolType(0) }, ref vlan_outer = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan_inner = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref ipv4 = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 60781, identification: 0, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(0), time_to_live: 0, protocol: 253 (Use for experimentation and testing), header_checksum: 128, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ref udp = UdpHeader { source_port: 24402, destination_port: 23948, length: 49051, checksum: 43062 } +cc c9232de04a5ac1b216ee2a446fc52503748be2b45c1d1622e9775e408e371b05 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0x0000 }, ref linux_sll = LinuxSllHeader { packet_type: 0 (Sent to us), arp_hrd_type: 824 (Netlink header), sender_address_valid_length: 0, sender_address: [0, 0, 0, 0, 0, 0, 0, 0], protocol_type: NetlinkProtocolType(0) }, ref vlan_outer = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan_inner = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x88E5 (IEEE Std 802.1AE - Media Access Control Security) }, ref ipv4 = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 116, identification: 0, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(0), time_to_live: 0, protocol: 249, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ref udp = UdpHeader { source_port: 31477, destination_port: 47928, length: 514, checksum: 40807 } +cc c63a66c0c25668d61d2b797e8999efbb3b360389e87888b75b713bffee230ae4 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0x0000 }, ether_type = 0x0001, ref linux_sll = LinuxSllHeader { packet_type: 0 (Sent to us), arp_hrd_type: 824 (Netlink header), sender_address_valid_length: 0, sender_address: [0, 0, 0, 0, 0, 0, 0, 0], protocol_type: NetlinkProtocolType(0) }, ref vlan_outer = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan_inner = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref macsec = MacsecHeader { ptype: Unmodified(0x0000), endstation_id: false, scb: false, an: MacsecAn(0), short_len: MacsecShortLen(0), packet_nr: 0, sci: None }, ref ipv4 = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 42578, identification: 0, dont_fragment: true, more_fragments: true, fragment_offset: IpFragOffset(8186), time_to_live: 4, protocol: 245, header_checksum: 48900, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ref udp = UdpHeader { source_port: 6925, destination_port: 44508, length: 6037, checksum: 64107 } diff --git a/etherparse/src/compositions_tests.rs b/etherparse/src/compositions_tests.rs index ba3dd8e9..96645825 100644 --- a/etherparse/src/compositions_tests.rs +++ b/etherparse/src/compositions_tests.rs @@ -1,40 +1,40 @@ use super::*; use crate::test_gens::*; -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; +use arrayvec::ArrayVec; use proptest::prelude::*; #[derive(Clone, Debug, Eq, PartialEq)] -struct ComponentTest { +struct ComponentTest<'a> { link: Option, - vlan: Option, + link_exts: ArrayVec, net: Option, transport: Option, - payload: Vec, + payload: &'a [u8], } -static VLAN_ETHER_TYPES: &'static [EtherType] = &[ - EtherType::VLAN_TAGGED_FRAME, - EtherType::PROVIDER_BRIDGING, - EtherType::VLAN_DOUBLE_TAGGED_FRAME, -]; - -impl ComponentTest { +impl<'a> ComponentTest<'a> { fn serialize(&self) -> Vec { let mut buffer = Vec::::with_capacity( match &self.link { Some(header) => header.header_len(), None => 0, - } + match &self.vlan { - Some(header) => header.header_len(), - None => 0, - } + match &self.net { - Some(headers) => headers.header_len(), - None => 0, - } + match &self.transport { - Some(header) => header.header_len(), - None => 0, - } + self.payload.len(), + } + self + .link_exts + .as_ref() + .iter() + .map(|v| v.header_len()) + .sum::() + + match &self.net { + Some(headers) => headers.header_len(), + None => 0, + } + + match &self.transport { + Some(header) => header.header_len(), + None => 0, + } + + self.payload.len(), ); //fill all the elements @@ -42,11 +42,11 @@ impl ComponentTest { Some(header) => header.write(&mut buffer).unwrap(), None => {} } - use crate::VlanHeader::*; - match &self.vlan { - Some(Single(header)) => header.write(&mut buffer).unwrap(), - Some(Double(header)) => header.write(&mut buffer).unwrap(), - None => {} + for e in &self.link_exts { + match e { + LinkExtHeader::Vlan(s) => s.write(&mut buffer).unwrap(), + LinkExtHeader::Macsec(m) => m.write(&mut buffer).unwrap(), + } } match &self.net { Some(NetHeaders::Ipv4(header, exts)) => { @@ -86,7 +86,57 @@ impl ComponentTest { // clone the test so the length fields can be adapted let mut test = self.clone(); - // set the payload length + // set ether types & macsec length + { + let mut next_ether_type = test.net.as_ref().map(|net| match net { + NetHeaders::Ipv4(_, _) => EtherType::IPV4, + NetHeaders::Ipv6(_, _) => EtherType::IPV6, + NetHeaders::Arp(_) => EtherType::ARP, + }); + let mut next_payload_len = test.net.as_ref().map(|net| net.header_len()).unwrap_or(0) + + test.transport.as_ref().map(|t| t.header_len()).unwrap_or(0) + + test.payload.len(); + + for ext in test.link_exts.iter_mut().rev() { + if let Some(e) = next_ether_type { + match ext { + LinkExtHeader::Vlan(vlan) => { + vlan.ether_type = e; + } + LinkExtHeader::Macsec(macsec) => { + macsec.ptype = MacsecPType::Unmodified(e); + macsec.set_payload_len(next_payload_len); + } + } + } else { + match ext { + LinkExtHeader::Vlan(_) => {} + LinkExtHeader::Macsec(macsec) => { + macsec.set_payload_len(next_payload_len); + } + } + } + next_ether_type = match ext { + LinkExtHeader::Vlan(_) => Some(EtherType::VLAN_TAGGED_FRAME), + LinkExtHeader::Macsec(_) => Some(EtherType::MACSEC), + }; + next_payload_len += ext.header_len(); + } + if let Some(link) = test.link.as_mut() { + if let Some(e) = next_ether_type { + match link { + LinkHeader::LinuxSll(sll) => { + sll.protocol_type = LinuxSllProtocolType::EtherType(e); + } + LinkHeader::Ethernet2(eth) => { + eth.ether_type = e; + } + } + } + } + } + + // set IP & ARP the payload length & last ether type if let Some(net) = test.net.as_mut() { match net { NetHeaders::Ipv4(ipv4, exts) => { @@ -108,6 +158,8 @@ impl ComponentTest { NetHeaders::Arp(_) => {} } } + + // set transport length if let Some(TransportHeader::Udp(udp)) = test.transport.as_mut() { udp.length = udp.header_len_u16() + self.payload.len() as u16; } @@ -185,7 +237,7 @@ impl ComponentTest { let ip_down = { let mut ip_down = test.clone(); ip_down.link = None; - ip_down.vlan = None; + ip_down.link_exts.clear(); ip_down }; @@ -234,13 +286,13 @@ impl ComponentTest { if let Some(link) = self.link.as_ref() { builder.add(link.header_len()); } - if let Some(vlan) = self.vlan.as_ref() { - use VlanHeader::*; - match vlan { - Single(single) => builder.add(single.header_len()), - Double(double) => { - builder.add(double.outer.header_len()); - builder.add(double.inner.header_len()); + for e in &self.link_exts { + match e { + LinkExtHeader::Vlan(s) => { + builder.add(s.header_len()); + } + LinkExtHeader::Macsec(m) => { + builder.add(m.header_len()); } } } @@ -288,7 +340,7 @@ impl ComponentTest { fn assert_headers(&self, actual: PacketHeaders) { assert_eq!(self.link, actual.link); - assert_eq!(self.vlan, actual.vlan); + assert_eq!(self.link_exts, actual.link_exts); assert_eq!(self.net, self.net); assert_eq!(self.transport, actual.transport); assert_eq!(self.payload[..], actual.payload.slice()[..]); @@ -298,7 +350,7 @@ impl ComponentTest { //assert identity to touch the derives (code coverage hack) assert_eq!(result, result); - //ethernet & vlan + //ethernet & link extensions assert_eq!( self.link, match result.link.as_ref() { @@ -310,8 +362,16 @@ impl ComponentTest { }, None => None, } - ); //.unwrap_or(None).map(|ref x| x.to_header())); - assert_eq!(self.vlan, result.vlan.as_ref().map(|ref x| x.to_header())); + ); + assert_eq!( + self.link_exts, + result + .link_exts + .as_ref() + .iter() + .map(|x| x.to_header()) + .collect::>() + ); //ip assert_eq!(self.net, { @@ -384,8 +444,10 @@ impl ComponentTest { } ); } else { - if let Some(vlan) = result.vlan.as_ref() { - assert_eq!(&self.payload[..], vlan.payload().payload); + if let Some(ext) = result.link_exts.last() { + if let Some(p) = ext.ether_payload() { + assert_eq!(&self.payload[..], p.payload); + } } else { if let Some(LinkSlice::Ethernet2(eth)) = result.link.as_ref() { assert_eq!(&self.payload[..], eth.payload().payload); @@ -396,10 +458,10 @@ impl ComponentTest { } } - fn run_vlan( + fn run_link_exts( &self, - outer_vlan: &SingleVlanHeader, - inner_vlan: &SingleVlanHeader, + vlans: &[SingleVlanHeader], + macsecs: &[MacsecHeader], arp: &ArpPacket, ipv4: &Ipv4Header, ipv4_ext: &Ipv4Extensions, @@ -410,54 +472,59 @@ impl ComponentTest { icmpv4: &Icmpv4Header, icmpv6: &Icmpv6Header, ) { - let setup_single = |ether_type: EtherType| -> ComponentTest { - let mut result = self.clone(); - result.vlan = Some(VlanHeader::Single({ - let mut v = inner_vlan.clone(); - v.ether_type = ether_type; - v - })); - result - }; - let setup_double = - |outer_ether_type: EtherType, inner_ether_type: EtherType| -> ComponentTest { - let mut result = self.clone(); - result.vlan = Some(VlanHeader::Double(DoubleVlanHeader { - outer: { - let mut v = outer_vlan.clone(); - v.ether_type = outer_ether_type; - v - }, - inner: { - let mut v = inner_vlan.clone(); - v.ether_type = inner_ether_type; - v - }, - })); - result + // add vlan + { + // build test + let test = { + let mut test = self.clone(); + test.link_exts + .try_push(LinkExtHeader::Vlan(vlans[0].clone())) + .unwrap(); + test }; + let vlans = &vlans[1..]; - //single - setup_single(inner_vlan.ether_type).run(); - setup_single(ether_type::ARP).run_arp(arp); - setup_single(ether_type::IPV4).run_ipv4(ipv4, ipv4_ext, udp, tcp, icmpv4, icmpv6); - setup_single(ether_type::IPV6).run_ipv6(ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6); - - //double - for ether_type in VLAN_ETHER_TYPES { - setup_double(*ether_type, inner_vlan.ether_type).run(); - setup_double(*ether_type, ether_type::ARP).run_arp(arp); - setup_double(*ether_type, ether_type::IPV4) - .run_ipv4(ipv4, ipv4_ext, udp, tcp, icmpv4, icmpv6); - setup_double(*ether_type, ether_type::IPV6) - .run_ipv6(ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6); + // run next steps + test.run(); + if !test.link_exts.is_full() { + test.run_link_exts( + vlans, macsecs, arp, ipv4, ipv4_ext, ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6, + ); + } + test.run_arp(arp); + test.run_ipv4(ipv4, ipv4_ext, udp, tcp, icmpv4, icmpv6); + test.run_ipv6(ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6); + } + + // add macsec + { + // build test + let test = { + let mut test = self.clone(); + test.link_exts + .try_push(LinkExtHeader::Macsec(macsecs[0].clone())) + .unwrap(); + test + }; + let macsecs = &macsecs[1..]; + + // run next steps + test.run(); + if !test.link_exts.is_full() { + test.run_link_exts( + vlans, macsecs, arp, ipv4, ipv4_ext, ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6, + ); + } + test.run_arp(arp); + test.run_ipv4(ipv4, ipv4_ext, udp, tcp, icmpv4, icmpv6); + test.run_ipv6(ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6); } } fn run_arp(&self, arp: &ArpPacket) { let mut test = self.clone(); test.net = Some(NetHeaders::Arp(arp.clone())); - test.payload.clear(); + test.payload = &[]; test.run(); } @@ -593,7 +660,13 @@ impl ComponentTest { .unwrap(); test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone())); // resize the payload in case it does not have to be as big - test.payload.resize(payload_size, 0); + let mut v = Vec::new(); + if payload_size <= test.payload.len() { + test.payload = &test.payload[..payload_size]; + } else { + v.resize(payload_size, 0); + test.payload = &v; + } test.run() } else { let mut test = self.clone(); @@ -616,7 +689,13 @@ impl ComponentTest { .unwrap(); test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone())); // resize the payload in case it does not have to be as big - test.payload.resize(payload_size, 0); + let mut v = Vec::new(); + if payload_size <= test.payload.len() { + test.payload = &test.payload[..payload_size]; + } else { + v.resize(payload_size, 0); + test.payload = &v; + } test.run() } else { let mut test = self.clone(); @@ -634,10 +713,14 @@ impl ComponentTest { proptest! { ///Test that all known packet compositions are parsed correctly. #[test] - #[cfg_attr(miri, ignore)] // vec allocation reduces miri runspeed too much + // #[cfg_attr(miri, ignore)] // vec allocation reduces miri runspeed too much fn test_compositions(ref eth in ethernet_2_unknown(), - ref vlan_outer in vlan_single_unknown(), - ref vlan_inner in vlan_single_unknown(), + ref vlan0 in vlan_single_unknown(), + ref vlan1 in vlan_single_unknown(), + ref vlan2 in vlan_single_unknown(), + ref macsec0 in macsec_unknown(), + ref macsec1 in macsec_unknown(), + ref macsec2 in macsec_unknown(), ref ipv4 in ipv4_unknown(), ref ipv4_exts in ipv4_extensions_unknown(), ref ipv6 in ipv6_unknown(), @@ -649,29 +732,27 @@ proptest! { ref icmpv6 in icmpv6_header_any(), ref payload in proptest::collection::vec(any::(), 0..1024)) { - let setup_eth = | ether_type: EtherType | -> ComponentTest { + let setup_eth = || -> ComponentTest { ComponentTest { - payload: payload.clone(), - link: Some({ - let mut result = eth.clone(); - result.ether_type = ether_type; - LinkHeader::Ethernet2(result) - }), - vlan: None, + payload: &payload, + link: Some(LinkHeader::Ethernet2(eth.clone())), + link_exts: ArrayVec::new_const(), net: None, transport: None } }; - //ethernet 2: standalone, ipv4, ipv6 - setup_eth(eth.ether_type).run(); - setup_eth(ether_type::ARP).run_arp(arp); - setup_eth(ether_type::IPV4).run_ipv4(ipv4, ipv4_exts, udp, tcp, icmpv4, icmpv6); - setup_eth(ether_type::IPV6).run_ipv6(ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6); + // ethernet 2: standalone, ipv4, ipv6 + setup_eth().run(); + setup_eth().run_arp(arp); + setup_eth().run_ipv4(ipv4, ipv4_exts, udp, tcp, icmpv4, icmpv6); + setup_eth().run_ipv6(ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6); - //vlans - for ether_type in VLAN_ETHER_TYPES { - setup_eth(*ether_type).run_vlan(vlan_outer, vlan_inner, arp, ipv4, ipv4_exts, ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6); + // link exts + { + let vlans = [vlan0.clone(), vlan1.clone(), vlan2.clone()]; + let macsecs = [macsec0.clone(), macsec1.clone(), macsec2.clone()]; + setup_eth().run_link_exts(&vlans[..], &macsecs[..], arp, ipv4, ipv4_exts, ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6); } } } @@ -682,7 +763,7 @@ proptest! { fn test_packet_slicing_panics() { let s = SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; @@ -692,10 +773,10 @@ fn test_packet_slicing_panics() { destination: [0; 6], ether_type: 0.into(), })), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, - payload: vec![], + payload: &[], } .assert_sliced_packet(s); } diff --git a/etherparse/src/defrag/ip_defrag_pool.rs b/etherparse/src/defrag/ip_defrag_pool.rs index c5d342d8..e5d1e780 100644 --- a/etherparse/src/defrag/ip_defrag_pool.rs +++ b/etherparse/src/defrag/ip_defrag_pool.rs @@ -64,19 +64,9 @@ where return Ok(None); } - let (outer_vlan_id, inner_vlan_id) = match &slice.vlan { - Some(VlanSlice::SingleVlan(s)) => (Some(s.vlan_identifier()), None), - Some(VlanSlice::DoubleVlan(d)) => ( - Some(d.outer().vlan_identifier()), - Some(d.inner().vlan_identifier()), - ), - None => (None, None), - }; - ( IpFragId { - outer_vlan_id, - inner_vlan_id, + vlan_ids: slice.vlan_ids(), ip: IpFragVersionSpecId::Ipv4 { source: header.source(), destination: header.destination(), @@ -115,20 +105,10 @@ where } }; - let (outer_vlan_id, inner_vlan_id) = match &slice.vlan { - Some(VlanSlice::SingleVlan(s)) => (Some(s.vlan_identifier()), None), - Some(VlanSlice::DoubleVlan(d)) => ( - Some(d.outer().vlan_identifier()), - Some(d.inner().vlan_identifier()), - ), - None => (None, None), - }; - // calculate frag id ( IpFragId { - outer_vlan_id, - inner_vlan_id, + vlan_ids: slice.vlan_ids(), ip: IpFragVersionSpecId::Ipv6 { source: ipv6.header().source(), destination: ipv6.header().destination(), @@ -251,6 +231,8 @@ where mod test { use std::cmp::max; + use arrayvec::ArrayVec; + use super::*; #[test] @@ -319,22 +301,22 @@ mod test { &Ethernet2Header { source: [0; 6], destination: [0; 6], - ether_type: if id.outer_vlan_id.is_some() || id.inner_vlan_id.is_some() { - EtherType::VLAN_TAGGED_FRAME - } else { + ether_type: if id.vlan_ids.is_empty() { ip_ether_type + } else { + EtherType::VLAN_TAGGED_FRAME }, } .to_bytes(), ); - if let Some(vlan_id) = id.outer_vlan_id { + for (index, vlan_id) in id.vlan_ids.iter().enumerate() { buf.extend_from_slice( &SingleVlanHeader { pcp: VlanPcp::try_new(0).unwrap(), drop_eligible_indicator: false, - vlan_id, - ether_type: if id.inner_vlan_id.is_some() { + vlan_id: *vlan_id, + ether_type: if index < id.vlan_ids.len() - 1 { EtherType::VLAN_TAGGED_FRAME } else { ip_ether_type @@ -344,18 +326,6 @@ mod test { ); } - if let Some(vlan_id) = id.inner_vlan_id { - buf.extend_from_slice( - &SingleVlanHeader { - pcp: VlanPcp::try_new(0).unwrap(), - drop_eligible_indicator: false, - vlan_id, - ether_type: ip_ether_type, - } - .to_bytes(), - ); - } - match id.ip { IpFragVersionSpecId::Ipv4 { source, @@ -423,7 +393,7 @@ mod test { let mut pool = IpDefragPool::<(), ()>::new(); let pslice = SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; @@ -439,8 +409,7 @@ mod test { let mut pool = IpDefragPool::<(), ()>::new(); let pdata = build_packet( IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv4 { source: [0; 4], destination: [0; 4], @@ -474,8 +443,7 @@ mod test { let mut pool = IpDefragPool::<(), ()>::new(); let pdata = build_packet( IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv6 { source: [0; 16], destination: [0; 16], @@ -509,8 +477,7 @@ mod test { let frag_ids = [ // v4 (no vlan) IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], @@ -521,8 +488,11 @@ mod test { }, // v4 (single vlan) IpFragId { - outer_vlan_id: Some(VlanId::try_new(12).unwrap()), - inner_vlan_id: None, + vlan_ids: { + let mut vlan_ids = ArrayVec::new_const(); + vlan_ids.push(VlanId::try_new(12).unwrap()); + vlan_ids + }, ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], @@ -533,8 +503,12 @@ mod test { }, // v4 (double vlan) IpFragId { - outer_vlan_id: Some(VlanId::try_new(12).unwrap()), - inner_vlan_id: Some(VlanId::try_new(23).unwrap()), + vlan_ids: { + let mut vlan_ids = ArrayVec::new_const(); + vlan_ids.push(VlanId::try_new(12).unwrap()); + vlan_ids.push(VlanId::try_new(13).unwrap()); + vlan_ids + }, ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], @@ -545,8 +519,7 @@ mod test { }, // v6 (no vlan) IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv6 { source: [0; 16], destination: [0; 16], @@ -557,8 +530,11 @@ mod test { }, // v6 (single vlan) IpFragId { - outer_vlan_id: Some(VlanId::try_new(12).unwrap()), - inner_vlan_id: None, + vlan_ids: { + let mut vlan_ids = ArrayVec::new_const(); + vlan_ids.push(VlanId::try_new(12).unwrap()); + vlan_ids + }, ip: IpFragVersionSpecId::Ipv6 { source: [0; 16], destination: [0; 16], @@ -569,8 +545,12 @@ mod test { }, // v6 (double vlan) IpFragId { - outer_vlan_id: Some(VlanId::try_new(12).unwrap()), - inner_vlan_id: Some(VlanId::try_new(23).unwrap()), + vlan_ids: { + let mut vlan_ids = ArrayVec::new_const(); + vlan_ids.push(VlanId::try_new(12).unwrap()); + vlan_ids.push(VlanId::try_new(13).unwrap()); + vlan_ids + }, ip: IpFragVersionSpecId::Ipv6 { source: [0; 16], destination: [0; 16], @@ -702,8 +682,7 @@ mod test { let frag_ids = [ // v4 IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], @@ -714,8 +693,7 @@ mod test { }, // v6 IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv6 { source: [0; 16], destination: [0; 16], @@ -747,8 +725,7 @@ mod test { let frag_ids = [ // v4 IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], @@ -759,8 +736,7 @@ mod test { }, // v6 IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv6 { source: [0; 16], destination: [0; 16], @@ -821,8 +797,7 @@ mod test { #[test] fn retain() { let frag_id_0 = IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], @@ -832,8 +807,7 @@ mod test { channel_id: (), }; let frag_id_1 = IpFragId { - outer_vlan_id: None, - inner_vlan_id: None, + vlan_ids: Default::default(), ip: IpFragVersionSpecId::Ipv4 { source: [1, 2, 3, 4], destination: [5, 6, 7, 8], diff --git a/etherparse/src/defrag/ip_frag_id.rs b/etherparse/src/defrag/ip_frag_id.rs index 865d3f66..4a65aa9f 100644 --- a/etherparse/src/defrag/ip_frag_id.rs +++ b/etherparse/src/defrag/ip_frag_id.rs @@ -1,4 +1,5 @@ use crate::{defrag::*, *}; +use arrayvec::ArrayVec; /// Values identifying a fragmented packet. #[derive(Debug, Clone, Hash, Eq, PartialEq)] @@ -6,11 +7,8 @@ pub struct IpFragId where CustomChannelId: core::hash::Hash + Eq + PartialEq + Clone + Sized, { - /// First VLAN id of the fragmented packets. - pub outer_vlan_id: Option, - - /// Second VLAN id of the fragmented packets. - pub inner_vlan_id: Option, + /// VLAN id's of the original packets. + pub vlan_ids: ArrayVec, /// IP source & destination address & identifaction field. pub ip: IpFragVersionSpecId, diff --git a/etherparse/src/err/double_vlan/header_error.rs b/etherparse/src/err/double_vlan/header_error.rs deleted file mode 100644 index f38b3d9e..00000000 --- a/etherparse/src/err/double_vlan/header_error.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::EtherType; - -/// Errors in an double vlan header encountered while decoding it. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum HeaderError { - /// Error when two vlan header were expected but the ether_type - /// value of the first vlan header is not an vlan header type. - NonVlanEtherType { - /// Non-VLAN ether type encountered in the outer vlan - /// header. - unexpected_ether_type: EtherType, - }, -} - -impl core::fmt::Display for HeaderError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - use HeaderError::*; - match self { - NonVlanEtherType { unexpected_ether_type } => write!(f, "Double VLAN Error: Expected two VLAN headers but the outer VLAN header is followed by a non-VLAN header of ether type {:?}.", unexpected_ether_type), - } - } -} - -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for HeaderError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -#[cfg(test)] -mod tests { - use super::HeaderError::*; - use crate::EtherType; - use alloc::format; - use std::{ - collections::hash_map::DefaultHasher, - error::Error, - hash::{Hash, Hasher}, - }; - - #[test] - fn debug() { - assert_eq!( - format!( - "NonVlanEtherType {{ unexpected_ether_type: {:?} }}", - EtherType(1) - ), - format!( - "{:?}", - NonVlanEtherType { - unexpected_ether_type: 1.into() - } - ) - ); - } - - #[test] - fn clone_eq_hash() { - let err = NonVlanEtherType { - unexpected_ether_type: 1.into(), - }; - assert_eq!(err, err.clone()); - let hash_a = { - let mut hasher = DefaultHasher::new(); - err.hash(&mut hasher); - hasher.finish() - }; - let hash_b = { - let mut hasher = DefaultHasher::new(); - err.clone().hash(&mut hasher); - hasher.finish() - }; - assert_eq!(hash_a, hash_b); - } - - #[test] - fn fmt() { - assert_eq!( - "Double VLAN Error: Expected two VLAN headers but the outer VLAN header is followed by a non-VLAN header of ether type 0x0001.", - format!("{}", NonVlanEtherType{ unexpected_ether_type: 1.into() }) - ); - } - - #[cfg(feature = "std")] - #[test] - fn source() { - assert!(NonVlanEtherType { - unexpected_ether_type: 1.into() - } - .source() - .is_none()); - } -} diff --git a/etherparse/src/err/from_slice_error.rs b/etherparse/src/err/from_slice_error.rs index fae13831..d81cd521 100644 --- a/etherparse/src/err/from_slice_error.rs +++ b/etherparse/src/err/from_slice_error.rs @@ -16,8 +16,8 @@ pub enum FromSliceError { /// Error when decoding an Linux SLL header. LinuxSll(linux_sll::HeaderError), - /// Error while parsing a double vlan header. - DoubleVlan(double_vlan::HeaderError), + /// Error when decoding MACsec header. + Macsec(macsec::HeaderError), /// Error while parsing a IP header. Ip(ip::HeaderError), @@ -51,9 +51,9 @@ impl FromSliceError { _ => None, } } - pub fn double_vlan(&self) -> Option<&double_vlan::HeaderError> { + pub fn macsec(&self) -> Option<&macsec::HeaderError> { match self { - FromSliceError::DoubleVlan(err) => Some(err), + FromSliceError::Macsec(err) => Some(err), _ => None, } } @@ -101,7 +101,7 @@ impl core::fmt::Display for FromSliceError { match self { Len(err) => err.fmt(f), LinuxSll(err) => err.fmt(f), - DoubleVlan(err) => err.fmt(f), + Macsec(err) => err.fmt(f), Ip(err) => err.fmt(f), IpAuth(err) => err.fmt(f), Ipv4(err) => err.fmt(f), @@ -116,16 +116,17 @@ impl core::fmt::Display for FromSliceError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for FromSliceError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use FromSliceError::*; match self { - FromSliceError::Len(err) => Some(err), - FromSliceError::LinuxSll(err) => Some(err), - FromSliceError::DoubleVlan(err) => Some(err), - FromSliceError::Ip(err) => Some(err), - FromSliceError::IpAuth(err) => Some(err), - FromSliceError::Ipv4(err) => Some(err), - FromSliceError::Ipv6(err) => Some(err), - FromSliceError::Ipv6Exts(err) => Some(err), - FromSliceError::Tcp(err) => Some(err), + Len(err) => Some(err), + LinuxSll(err) => Some(err), + Macsec(err) => Some(err), + Ip(err) => Some(err), + IpAuth(err) => Some(err), + Ipv4(err) => Some(err), + Ipv6(err) => Some(err), + Ipv6Exts(err) => Some(err), + Tcp(err) => Some(err), } } } @@ -156,24 +157,6 @@ impl From for FromSliceError { } } -// double vlan error conversions - -impl From for FromSliceError { - fn from(value: double_vlan::HeaderError) -> Self { - FromSliceError::DoubleVlan(value) - } -} - -impl From for FromSliceError { - fn from(value: double_vlan::HeaderSliceError) -> Self { - use double_vlan::HeaderSliceError::*; - match value { - Len(err) => FromSliceError::Len(err), - Content(err) => FromSliceError::DoubleVlan(err), - } - } -} - // ip error conversions impl From for FromSliceError { @@ -314,6 +297,7 @@ impl From for FromSliceError { match value { Len(err) => FromSliceError::Len(err), LinuxSll(err) => FromSliceError::LinuxSll(err), + Macsec(err) => FromSliceError::Macsec(err), Ip(err) => FromSliceError::Ip(err), Ipv4(err) => FromSliceError::Ipv4(err), Ipv6(err) => FromSliceError::Ipv6(err), @@ -344,7 +328,7 @@ impl From for FromSliceError { #[cfg(test)] mod tests { - use crate::{ArpHardwareId, EtherType, LenSource}; + use crate::{ArpHardwareId, LenSource}; use super::{FromSliceError::*, *}; use core::hash::{Hash, Hasher}; @@ -377,7 +361,7 @@ mod tests { #[test] fn debug_source() { - let test_values: [(&str, FromSliceError); 8] = [ + let test_values: [(&str, FromSliceError); 9] = [ ( "Len", Len(LenError { @@ -389,11 +373,12 @@ mod tests { }), ), ( - "DoubleVlan", - DoubleVlan(double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(123), + "LinuxSll", + LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId { + arp_hardware_type: ArpHardwareId(0), }), ), + ("Macsec", Macsec(macsec::HeaderError::UnexpectedVersion)), ( "Ip", Ip(ip::HeaderError::UnsupportedIpVersion { @@ -440,9 +425,7 @@ mod tests { LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId { arp_hardware_type: ArpHardwareId::ETHERNET, }), - DoubleVlan(double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(123), - }), + Macsec(macsec::HeaderError::UnexpectedVersion), Ip(ip::HeaderError::UnsupportedIpVersion { version_number: 123, }), @@ -471,9 +454,7 @@ mod tests { let linux_sll_error = || linux_sll::HeaderError::UnsupportedArpHardwareId { arp_hardware_type: ArpHardwareId::ETHERNET, }; - let double_vlan_error = || double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(1), - }; + let macsec_error = || macsec::HeaderError::UnexpectedVersion; let ip_error = || ip::HeaderError::UnsupportedIpVersion { version_number: 0 }; let ipv4_error = || ipv4::HeaderError::UnexpectedVersion { version_number: 1 }; let ipv6_error = || ipv6::HeaderError::UnexpectedVersion { version_number: 1 }; @@ -492,12 +473,9 @@ mod tests { ); assert_eq!(Ipv4(ipv4_error()).linux_sll(), None); - // double_vlan - assert_eq!( - DoubleVlan(double_vlan_error()).double_vlan(), - Some(&double_vlan_error()) - ); - assert_eq!(Ipv4(ipv4_error()).double_vlan(), None); + // macsec + assert_eq!(Macsec(macsec_error()).macsec(), Some(&macsec_error())); + assert_eq!(Ipv4(ipv4_error()).macsec(), None); // ip assert_eq!(Ip(ip_error()).ip(), Some(&ip_error())); @@ -574,40 +552,15 @@ mod tests { ); } - // double vlan errors - { - let header_error = || double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(123), - }; - assert_eq!( - &header_error(), - FromSliceError::from(header_error()).double_vlan().unwrap() - ); - assert_eq!( - &header_error(), - FromSliceError::from(double_vlan::HeaderSliceError::Content(header_error())) - .double_vlan() - .unwrap() - ); - assert_eq!( - &len_error(), - FromSliceError::from(double_vlan::HeaderSliceError::Len(len_error())) - .len() - .unwrap() - ); - assert_eq!( - &header_error(), - FromSliceError::from(double_vlan::HeaderSliceError::Content(header_error())) - .double_vlan() - .unwrap() - ); - } - // ip errors { let header_error = || ip::HeaderError::UnsupportedIpVersion { version_number: 123, }; + + let ip_auth_error = || ip_auth::HeaderError::ZeroPayloadLen; + let ipv6_ext_header_error = || ipv6_exts::HeaderError::HopByHopNotAtStart; + assert_eq!( &header_error(), FromSliceError::from(header_error()).ip().unwrap() @@ -620,6 +573,22 @@ mod tests { .ip() .unwrap() ); + assert_eq!( + &ip_auth_error(), + FromSliceError::from(ip::HeadersSliceError::Content(ip::HeadersError::Ipv4Ext( + ip_auth_error() + ))) + .ip_auth() + .unwrap() + ); + assert_eq!( + &ipv6_ext_header_error(), + FromSliceError::from(ip::HeadersSliceError::Content(ip::HeadersError::Ipv6Ext( + ipv6_ext_header_error() + ))) + .ipv6_exts() + .unwrap() + ); assert_eq!( &len_error(), FromSliceError::from(ip::HeadersSliceError::Len(len_error())) @@ -794,6 +763,10 @@ mod tests { // packet error { + let linux_sll_error = || linux_sll::HeaderError::UnsupportedArpHardwareId { + arp_hardware_type: ArpHardwareId(0), + }; + let macsec_error = || macsec::HeaderError::UnexpectedVersion; let ip_error = || ip::HeaderError::UnsupportedIpVersion { version_number: 0 }; let ipv4_error = || ipv4::HeaderError::UnexpectedVersion { version_number: 1 }; let ipv6_error = || ipv6::HeaderError::UnexpectedVersion { version_number: 1 }; @@ -808,6 +781,18 @@ mod tests { .len() .unwrap() ); + assert_eq!( + &linux_sll_error(), + FromSliceError::from(packet::SliceError::LinuxSll(linux_sll_error())) + .linux_sll() + .unwrap() + ); + assert_eq!( + &macsec_error(), + FromSliceError::from(packet::SliceError::Macsec(macsec_error())) + .macsec() + .unwrap() + ); assert_eq!( &ip_error(), FromSliceError::from(packet::SliceError::Ip(ip_error())) diff --git a/etherparse/src/err/layer.rs b/etherparse/src/err/layer.rs index a6b3c79a..d977fb29 100644 --- a/etherparse/src/err/layer.rs +++ b/etherparse/src/err/layer.rs @@ -9,6 +9,11 @@ pub enum Layer { EtherPayload, /// Error occurred in the vlan header. VlanHeader, + /// Error occurred in the MACsec header (also known as Sectag). + MacsecHeader, + /// Error occurred while verifying the total MACsec packet length is long enough + /// for the "short length". + MacsecPacket, /// Error occurred when decoding an IP header (v4 or v6). IpHeader, /// Error occurred in the IPv4 layer. @@ -58,6 +63,8 @@ impl Layer { Ethernet2Header => "Ethernet 2 Header Error", EtherPayload => "Payload with Ether Type Error", VlanHeader => "VLAN Header Error", + MacsecHeader => "MACsec Header Error", + MacsecPacket => "MACsec Packet Error", IpHeader => "IP Header Error", Ipv4Header => "IPv4 Header Error", Ipv4Packet => "IPv4 Packet Error", @@ -89,6 +96,8 @@ impl core::fmt::Display for Layer { Ethernet2Header => write!(f, "Ethernet 2 header"), EtherPayload => write!(f, "Ether type payload"), VlanHeader => write!(f, "VLAN header"), + MacsecHeader => write!(f, "MACsec header"), + MacsecPacket => write!(f, "MACsec packet"), IpHeader => write!(f, "IP header"), Ipv4Header => write!(f, "IPv4 header"), Ipv4Packet => write!(f, "IPv4 packet"), @@ -153,6 +162,8 @@ mod test { (Ethernet2Header, "Ethernet 2 Header Error"), (EtherPayload, "Payload with Ether Type Error"), (VlanHeader, "VLAN Header Error"), + (MacsecHeader, "MACsec Header Error"), + (MacsecPacket, "MACsec Packet Error"), (IpHeader, "IP Header Error"), (Ipv4Header, "IPv4 Header Error"), (Ipv4Packet, "IPv4 Packet Error"), @@ -174,6 +185,7 @@ mod test { (Icmpv4Timestamp, "ICMP Timestamp Error"), (Icmpv4TimestampReply, "ICMP Timestamp Reply Error"), (Icmpv6, "ICMPv6 Packet Error"), + (Arp, "Address Resolution Protocol Packet Error"), ]; for test in tests { assert_eq!(test.0.error_title(), test.1); @@ -187,6 +199,8 @@ mod test { (Ethernet2Header, "Ethernet 2 header"), (EtherPayload, "Ether type payload"), (VlanHeader, "VLAN header"), + (MacsecHeader, "MACsec header"), + (MacsecPacket, "MACsec packet"), (IpHeader, "IP header"), (Ipv4Header, "IPv4 header"), (Ipv4Packet, "IPv4 packet"), @@ -205,6 +219,7 @@ mod test { (Icmpv4Timestamp, "ICMP timestamp message"), (Icmpv4TimestampReply, "ICMP timestamp reply message"), (Icmpv6, "ICMPv6 packet"), + (Arp, "Address Resolution Protocol packet"), ]; for test in tests { assert_eq!(format!("{}", test.0), test.1); diff --git a/etherparse/src/err/len_error.rs b/etherparse/src/err/len_error.rs index 0ff1ff8e..5417624f 100644 --- a/etherparse/src/err/len_error.rs +++ b/etherparse/src/err/len_error.rs @@ -78,6 +78,9 @@ impl core::fmt::Display for LenError { use LenSource::*; match self.len_source { Slice => "slice length", + MacsecShortLength => { + "length calculated from the MACsec header 'short length' field" + } Ipv4HeaderTotalLen => "length calculated from the IPv4 header 'total length' field", Ipv6HeaderPayloadLen => { "length calculated from the IPv6 header 'payload length' field" diff --git a/etherparse/src/err/macsec/header_error.rs b/etherparse/src/err/macsec/header_error.rs new file mode 100644 index 00000000..4410c467 --- /dev/null +++ b/etherparse/src/err/macsec/header_error.rs @@ -0,0 +1,82 @@ +/// Error when decoding a MACsec header. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum HeaderError { + /// Error when the MACsec header version field is not equal 0. + UnexpectedVersion, + + /// Error if the short len is 1 when it should be at least 2 + /// (for the next ether type). + InvalidUnmodifiedShortLen, +} + +impl core::fmt::Display for HeaderError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use HeaderError::*; + match self { + UnexpectedVersion => write!(f, "MACsec Header Error: Encountered '1' as MACsec version in the MACsec SecTag header (must be '0')."), + InvalidUnmodifiedShortLen => write!(f, "MACsec Header Error: Encountered '1' as MACsec short len in an unmodified packet (must be '0' or '2' or greater)."), + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for HeaderError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +#[cfg(test)] +mod tests { + use super::{HeaderError::*, *}; + use alloc::format; + use std::{ + collections::hash_map::DefaultHasher, + error::Error, + hash::{Hash, Hasher}, + }; + + #[test] + fn debug() { + assert_eq!("UnexpectedVersion", format!("{:?}", UnexpectedVersion)); + } + + #[test] + fn clone_eq_hash() { + let err = HeaderError::UnexpectedVersion; + assert_eq!(err, err.clone()); + let hash_a = { + let mut hasher = DefaultHasher::new(); + err.hash(&mut hasher); + hasher.finish() + }; + let hash_b = { + let mut hasher = DefaultHasher::new(); + err.clone().hash(&mut hasher); + hasher.finish() + }; + assert_eq!(hash_a, hash_b); + } + + #[test] + fn fmt() { + assert_eq!( + "MACsec Header Error: Encountered '1' as MACsec version in the MACsec SecTag header (must be '0').", + format!("{}", UnexpectedVersion) + ); + assert_eq!( + "MACsec Header Error: Encountered '1' as MACsec short len in an unmodified packet (must be '0' or '2' or greater).", + format!("{}", InvalidUnmodifiedShortLen) + ); + } + + #[cfg(feature = "std")] + #[test] + fn source() { + let values = [UnexpectedVersion, InvalidUnmodifiedShortLen]; + for v in values { + assert!(v.source().is_none()); + } + } +} diff --git a/etherparse/src/err/double_vlan/header_read_error.rs b/etherparse/src/err/macsec/header_read_error.rs similarity index 75% rename from etherparse/src/err/double_vlan/header_read_error.rs rename to etherparse/src/err/macsec/header_read_error.rs index e24c2437..e002b265 100644 --- a/etherparse/src/err/double_vlan/header_read_error.rs +++ b/etherparse/src/err/macsec/header_read_error.rs @@ -1,8 +1,6 @@ use super::HeaderError; -/// Error when decoding two VLAN headers via a `std::io::Read` source. -/// -/// Requires crate feature `std`. +/// Error when decoding an MACsec header via a `std::io::Read` source. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[derive(Debug)] @@ -28,7 +26,7 @@ impl HeaderReadError { } } - /// Returns the `err::double_vlan::HeaderError` value if the `HeaderReadError` is `Content`. + /// Returns the `err::ipv4::HeaderError` value if the `HeaderReadError` is `Content`. /// Otherwise `None` is returned. #[inline] pub fn content_error(self) -> Option { @@ -46,7 +44,7 @@ impl core::fmt::Display for HeaderReadError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use HeaderReadError::*; match self { - Io(err) => write!(f, "Double VLAN Header IO Error: {}", err), + Io(err) => write!(f, "IPv4 Header IO Error: {}", err), Content(value) => value.fmt(f), } } @@ -71,9 +69,7 @@ mod test { #[test] fn debug() { - let err = HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into(), - }; + let err = HeaderError::UnexpectedVersion; assert_eq!( format!("Content({:?})", err.clone()), format!("{:?}", Content(err)) @@ -88,14 +84,12 @@ mod test { "failed to fill whole buffer", ); assert_eq!( - format!("Double VLAN Header IO Error: {}", err), + format!("IPv4 Header IO Error: {}", err), format!("{}", Io(err)) ); } { - let err = HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into(), - }; + let err = HeaderError::UnexpectedVersion; assert_eq!(format!("{}", &err), format!("{}", Content(err.clone()))); } } @@ -109,11 +103,7 @@ mod test { )) .source() .is_some()); - assert!(Content(HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into() - }) - .source() - .is_some()); + assert!(Content(HeaderError::UnexpectedVersion).source().is_some()); } #[test] @@ -124,11 +114,7 @@ mod test { )) .io_error() .is_some()); - assert!(Content(HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into() - }) - .io_error() - .is_none()); + assert!(Content(HeaderError::UnexpectedVersion).io_error().is_none()); } #[test] @@ -142,9 +128,7 @@ mod test { .content_error() ); { - let err = HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into(), - }; + let err = HeaderError::UnexpectedVersion; assert_eq!(Some(err.clone()), Content(err.clone()).content_error()); } } diff --git a/etherparse/src/err/double_vlan/header_slice_error.rs b/etherparse/src/err/macsec/header_slice_error.rs similarity index 79% rename from etherparse/src/err/double_vlan/header_slice_error.rs rename to etherparse/src/err/macsec/header_slice_error.rs index 7b78eea5..5dc30d18 100644 --- a/etherparse/src/err/double_vlan/header_slice_error.rs +++ b/etherparse/src/err/macsec/header_slice_error.rs @@ -1,7 +1,7 @@ use super::HeaderError; use crate::err::LenError; -/// Error when decoding a double VLAN header from a slice. +/// Error when decoding a MACsec header from a slice. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum HeaderSliceError { /// Error when an length error is encountered (e.g. unexpected @@ -29,7 +29,7 @@ impl core::fmt::Display for HeaderSliceError { use HeaderSliceError::*; match self { Len(err) => err.fmt(f), - Content(err) => err.fmt(f), + Content(value) => value.fmt(f), } } } @@ -59,6 +59,7 @@ mod tests { #[test] fn add_slice_offset() { + use HeaderSliceError::*; assert_eq!( Len(LenError { required_len: 1, @@ -77,21 +78,14 @@ mod tests { }) ); assert_eq!( - Content(HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into() - }) - .add_slice_offset(200), - Content(HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into() - }) + Content(HeaderError::UnexpectedVersion).add_slice_offset(200), + Content(HeaderError::UnexpectedVersion) ); } #[test] fn debug() { - let err = HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into(), - }; + let err = HeaderError::UnexpectedVersion; assert_eq!( format!("Content({:?})", err.clone()), format!("{:?}", Content(err)) @@ -100,9 +94,7 @@ mod tests { #[test] fn clone_eq_hash() { - let err = Content(HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into(), - }); + let err = Content(HeaderError::UnexpectedVersion); assert_eq!(err, err.clone()); let hash_a = { let mut hasher = DefaultHasher::new(); @@ -130,9 +122,7 @@ mod tests { assert_eq!(format!("{}", &err), format!("{}", Len(err))); } { - let err = HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into(), - }; + let err = HeaderError::UnexpectedVersion; assert_eq!(format!("{}", &err), format!("{}", Content(err.clone()))); } } @@ -149,10 +139,6 @@ mod tests { }) .source() .is_some()); - assert!(Content(HeaderError::NonVlanEtherType { - unexpected_ether_type: 1.into() - }) - .source() - .is_some()); + assert!(Content(HeaderError::UnexpectedVersion).source().is_some()); } } diff --git a/etherparse/src/err/double_vlan/mod.rs b/etherparse/src/err/macsec/mod.rs similarity index 100% rename from etherparse/src/err/double_vlan/mod.rs rename to etherparse/src/err/macsec/mod.rs diff --git a/etherparse/src/err/mod.rs b/etherparse/src/err/mod.rs index 4d0b1d10..c955625c 100644 --- a/etherparse/src/err/mod.rs +++ b/etherparse/src/err/mod.rs @@ -1,5 +1,4 @@ pub mod arp; -pub mod double_vlan; #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub mod io; @@ -11,6 +10,7 @@ pub mod ipv4_exts; pub mod ipv6; pub mod ipv6_exts; pub mod linux_sll; +pub mod macsec; pub mod net; pub mod packet; pub mod tcp; diff --git a/etherparse/src/err/packet/slice_error.rs b/etherparse/src/err/packet/slice_error.rs index 5123c038..ab2bb1ee 100644 --- a/etherparse/src/err/packet/slice_error.rs +++ b/etherparse/src/err/packet/slice_error.rs @@ -8,6 +8,8 @@ pub enum SliceError { Len(err::LenError), /// Error when decoding an Linux SLL header. LinuxSll(err::linux_sll::HeaderError), + /// Error when decoding MACsec header. + Macsec(err::macsec::HeaderError), /// Error when decoding starting at an IP header (v4 or v6). Ip(err::ip::HeaderError), /// Error when decoding an IPv4 header. @@ -29,6 +31,7 @@ impl core::fmt::Display for SliceError { match self { Len(err) => err.fmt(f), LinuxSll(err) => err.fmt(f), + Macsec(err) => err.fmt(f), Ip(err) => err.fmt(f), Ipv4(err) => err.fmt(f), Ipv6(err) => err.fmt(f), @@ -47,6 +50,7 @@ impl std::error::Error for SliceError { match self { Len(err) => Some(err), LinuxSll(err) => Some(err), + Macsec(err) => Some(err), Ip(err) => Some(err), Ipv4(err) => Some(err), Ipv6(err) => Some(err), @@ -119,6 +123,15 @@ mod tests { ); } + // Macsec Header + { + let err = err::macsec::HeaderError::UnexpectedVersion; + assert_eq!( + format!("{}", err), + format!("{}", err::packet::SliceError::Macsec(err)) + ); + } + // IpHeader { let err = err::ip::HeaderError::UnsupportedIpVersion { version_number: 1 }; @@ -174,6 +187,22 @@ mod tests { assert!(Len(err).source().is_some()); } + // Linux SLL Header + { + let err = err::linux_sll::HeaderError::UnsupportedArpHardwareId { + arp_hardware_type: ArpHardwareId::ADAPT, + }; + assert!(err::packet::SliceError::LinuxSll(err.clone()) + .source() + .is_some()); + } + + // Macsec Header + { + let err = err::macsec::HeaderError::UnexpectedVersion; + assert!(err::packet::SliceError::Macsec(err).source().is_some()); + } + // IpHeaders { let err = err::linux_sll::HeaderError::UnsupportedArpHardwareId { diff --git a/etherparse/src/err/read_error.rs b/etherparse/src/err/read_error.rs index f0fb68aa..50152a03 100644 --- a/etherparse/src/err/read_error.rs +++ b/etherparse/src/err/read_error.rs @@ -18,8 +18,8 @@ pub enum ReadError { /// not enough data being available). Len(LenError), - /// Error while parsing a double vlan header. - DoubleVlan(double_vlan::HeaderError), + /// Error when decoding MACsec header. + Macsec(macsec::HeaderError), /// Error while parsing a IP header. Ip(ip::HeaderError), @@ -57,9 +57,9 @@ impl ReadError { _ => None, } } - pub fn double_vlan(&self) -> Option<&double_vlan::HeaderError> { + pub fn macsec(&self) -> Option<&macsec::HeaderError> { match self { - ReadError::DoubleVlan(err) => Some(err), + ReadError::Macsec(err) => Some(err), _ => None, } } @@ -113,7 +113,7 @@ impl core::fmt::Display for ReadError { match self { Io(err) => err.fmt(f), Len(err) => err.fmt(f), - DoubleVlan(err) => err.fmt(f), + Macsec(err) => err.fmt(f), Ip(err) => err.fmt(f), IpAuth(err) => err.fmt(f), Ipv4(err) => err.fmt(f), @@ -129,17 +129,18 @@ impl core::fmt::Display for ReadError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for ReadError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use ReadError::*; match self { - ReadError::Io(err) => Some(err), - ReadError::Len(err) => Some(err), - ReadError::DoubleVlan(err) => Some(err), - ReadError::Ip(err) => Some(err), - ReadError::IpAuth(err) => Some(err), - ReadError::Ipv4(err) => Some(err), - ReadError::Ipv6(err) => Some(err), - ReadError::Ipv6Exts(err) => Some(err), - ReadError::LinuxSll(err) => Some(err), - ReadError::Tcp(err) => Some(err), + Io(err) => Some(err), + Len(err) => Some(err), + Macsec(err) => Some(err), + Ip(err) => Some(err), + IpAuth(err) => Some(err), + Ipv4(err) => Some(err), + Ipv6(err) => Some(err), + Ipv6Exts(err) => Some(err), + LinuxSll(err) => Some(err), + Tcp(err) => Some(err), } } } @@ -159,36 +160,6 @@ impl From for ReadError { } } -// double vlan error conversions -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for ReadError { - fn from(value: double_vlan::HeaderError) -> Self { - ReadError::DoubleVlan(value) - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for ReadError { - fn from(value: double_vlan::HeaderReadError) -> Self { - use double_vlan::HeaderReadError::*; - match value { - Io(err) => ReadError::Io(err), - Content(err) => ReadError::DoubleVlan(err), - } - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for ReadError { - fn from(value: double_vlan::HeaderSliceError) -> Self { - use double_vlan::HeaderSliceError::*; - match value { - Len(err) => ReadError::Len(err), - Content(err) => ReadError::DoubleVlan(err), - } - } -} - // ip error conversions #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From for ReadError { @@ -416,6 +387,36 @@ impl From for ReadError { } } +// macsec error conversions +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for ReadError { + fn from(value: macsec::HeaderError) -> Self { + ReadError::Macsec(value) + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for ReadError { + fn from(value: macsec::HeaderReadError) -> Self { + use macsec::HeaderReadError::*; + match value { + Io(err) => ReadError::Io(err), + Content(err) => ReadError::Macsec(err), + } + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for ReadError { + fn from(value: macsec::HeaderSliceError) -> Self { + use macsec::HeaderSliceError::*; + match value { + Len(err) => ReadError::Len(err), + Content(err) => ReadError::Macsec(err), + } + } +} + // packet error conversions #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From for ReadError { @@ -424,6 +425,7 @@ impl From for ReadError { match value { Len(err) => ReadError::Len(err), LinuxSll(err) => ReadError::LinuxSll(err), + Macsec(err) => ReadError::Macsec(err), Ip(err) => ReadError::Ip(err), Ipv4(err) => ReadError::Ipv4(err), Ipv6(err) => ReadError::Ipv6(err), @@ -466,11 +468,11 @@ impl From for ReadError { #[cfg(test)] mod tests { + use crate::ArpHardwareId; use crate::{ err::{ReadError::*, *}, LenSource, }; - use crate::{ArpHardwareId, EtherType}; use std::error::Error; use std::format; @@ -493,12 +495,7 @@ mod tests { arp_hardware_type: ArpHardwareId::ETHERNET, }), ), - ( - "DoubleVlan", - DoubleVlan(double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(123), - }), - ), + ("Macsec", Macsec(macsec::HeaderError::UnexpectedVersion)), ( "Ip", Ip(ip::HeaderError::UnsupportedIpVersion { @@ -557,9 +554,7 @@ mod tests { LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId { arp_hardware_type: ArpHardwareId::ETHERNET, }), - DoubleVlan(double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(123), - }), + Macsec(macsec::HeaderError::UnexpectedVersion), Ip(ip::HeaderError::UnsupportedIpVersion { version_number: 123, }), @@ -593,9 +588,7 @@ mod tests { layer: Layer::Icmpv4, layer_start_offset: 0, }; - let double_vlan_error = || double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(1), - }; + let macsec_error = || macsec::HeaderError::UnexpectedVersion; let ip_error = || ip::HeaderError::UnsupportedIpVersion { version_number: 0 }; let ipv4_error = || ipv4::HeaderError::UnexpectedVersion { version_number: 1 }; let ipv6_error = || ipv6::HeaderError::UnexpectedVersion { version_number: 1 }; @@ -620,12 +613,9 @@ mod tests { ); assert_eq!(Ipv4(ipv4_error()).linux_sll(), None); - // double_vlan - assert_eq!( - DoubleVlan(double_vlan_error()).double_vlan(), - Some(&double_vlan_error()) - ); - assert_eq!(Ipv4(ipv4_error()).double_vlan(), None); + // macsec + assert_eq!(Macsec(macsec_error()).macsec(), Some(&macsec_error())); + assert_eq!(Ipv4(ipv4_error()).macsec(), None); // ip assert_eq!(Ip(ip_error()).ip(), Some(&ip_error())); @@ -718,42 +708,75 @@ mod tests { ); } - // double vlan errors + // linux_sll errors { - let header_error = || double_vlan::HeaderError::NonVlanEtherType { - unexpected_ether_type: EtherType(123), - }; + let header_error = + || linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 123 }; + assert_eq!( + &header_error(), + ReadError::from(header_error()).linux_sll().unwrap() + ); + assert_eq!( + &header_error(), + ReadError::from(linux_sll::HeaderReadError::Content(header_error())) + .linux_sll() + .unwrap() + ); + assert!(ReadError::from(linux_sll::HeaderReadError::Io(io_error())) + .io() + .is_some()); assert_eq!( &header_error(), - ReadError::from(header_error()).double_vlan().unwrap() + ReadError::from(linux_sll::HeaderSliceError::Content(header_error())) + .linux_sll() + .unwrap() + ); + assert_eq!( + &len_error(), + ReadError::from(linux_sll::HeaderSliceError::Len(len_error())) + .len() + .unwrap() ); assert_eq!( &header_error(), - ReadError::from(double_vlan::HeaderReadError::Content(header_error())) - .double_vlan() + ReadError::from(linux_sll::HeaderSliceError::Content(header_error())) + .linux_sll() .unwrap() ); - assert!( - ReadError::from(double_vlan::HeaderReadError::Io(io_error())) - .io() - .is_some() + } + + // macsec errors + { + let header_error = || macsec::HeaderError::UnexpectedVersion; + assert_eq!( + &header_error(), + ReadError::from(header_error()).macsec().unwrap() ); assert_eq!( &header_error(), - ReadError::from(double_vlan::HeaderSliceError::Content(header_error())) - .double_vlan() + ReadError::from(macsec::HeaderReadError::Content(header_error())) + .macsec() + .unwrap() + ); + assert!(ReadError::from(macsec::HeaderReadError::Io(io_error())) + .io() + .is_some()); + assert_eq!( + &header_error(), + ReadError::from(macsec::HeaderSliceError::Content(header_error())) + .macsec() .unwrap() ); assert_eq!( &len_error(), - ReadError::from(double_vlan::HeaderSliceError::Len(len_error())) + ReadError::from(macsec::HeaderSliceError::Len(len_error())) .len() .unwrap() ); assert_eq!( &header_error(), - ReadError::from(double_vlan::HeaderSliceError::Content(header_error())) - .double_vlan() + ReadError::from(macsec::HeaderSliceError::Content(header_error())) + .macsec() .unwrap() ); } @@ -763,6 +786,9 @@ mod tests { let header_error = || ip::HeaderError::UnsupportedIpVersion { version_number: 123, }; + let ip_auth_error = || ip_auth::HeaderError::ZeroPayloadLen; + let ipv6_ext_error = || ipv6_exts::HeaderError::HopByHopNotAtStart; + assert_eq!( &header_error(), ReadError::from(header_error()).ip().unwrap() @@ -775,6 +801,22 @@ mod tests { .ip() .unwrap() ); + assert_eq!( + &ip_auth_error(), + ReadError::from(ip::HeaderReadError::Content(ip::HeadersError::Ipv4Ext( + ip_auth_error() + ))) + .ip_auth() + .unwrap() + ); + assert_eq!( + &ipv6_ext_error(), + ReadError::from(ip::HeaderReadError::Content(ip::HeadersError::Ipv6Ext( + ipv6_ext_error() + ))) + .ipv6_exts() + .unwrap() + ); assert_eq!( &len_error(), ReadError::from(ip::HeaderReadError::Len(len_error())) @@ -1008,45 +1050,12 @@ mod tests { ); } - // linux_sll errors - { - let header_error = - || linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 123 }; - assert_eq!( - &header_error(), - ReadError::from(header_error()).linux_sll().unwrap() - ); - assert_eq!( - &header_error(), - ReadError::from(linux_sll::HeaderReadError::Content(header_error())) - .linux_sll() - .unwrap() - ); - assert!(ReadError::from(linux_sll::HeaderReadError::Io(io_error())) - .io() - .is_some()); - assert_eq!( - &header_error(), - ReadError::from(linux_sll::HeaderSliceError::Content(header_error())) - .linux_sll() - .unwrap() - ); - assert_eq!( - &len_error(), - ReadError::from(linux_sll::HeaderSliceError::Len(len_error())) - .len() - .unwrap() - ); - assert_eq!( - &header_error(), - ReadError::from(linux_sll::HeaderSliceError::Content(header_error())) - .linux_sll() - .unwrap() - ); - } - // packet error { + let linux_sll_error = || linux_sll::HeaderError::UnsupportedArpHardwareId { + arp_hardware_type: ArpHardwareId(0), + }; + let macsec_error = || macsec::HeaderError::UnexpectedVersion; let ip_error = || ip::HeaderError::UnsupportedIpVersion { version_number: 0 }; let ipv4_error = || ipv4::HeaderError::UnexpectedVersion { version_number: 1 }; let ipv6_error = || ipv6::HeaderError::UnexpectedVersion { version_number: 1 }; @@ -1061,6 +1070,18 @@ mod tests { .len() .unwrap() ); + assert_eq!( + &linux_sll_error(), + ReadError::from(packet::SliceError::LinuxSll(linux_sll_error())) + .linux_sll() + .unwrap() + ); + assert_eq!( + &macsec_error(), + ReadError::from(packet::SliceError::Macsec(macsec_error())) + .macsec() + .unwrap() + ); assert_eq!( &ip_error(), ReadError::from(packet::SliceError::Ip(ip_error())) diff --git a/etherparse/src/err/value_type.rs b/etherparse/src/err/value_type.rs index b5d8405a..046703dd 100644 --- a/etherparse/src/err/value_type.rs +++ b/etherparse/src/err/value_type.rs @@ -6,6 +6,10 @@ pub enum ValueType { VlanId, /// VLAN PCP (Priority Code Point) field in a [`crate::SingleVlanHeader`]. VlanPcp, + /// MACsec association number (present in the [`crate::MacSecHeader`]). + MacsecAn, + /// MACsec short length (present in the [`crate::MacSecHeader`]). + MacsecShortLen, /// IP Fragment offset present in the IPv4 header and /// IPv6 fragmentation header. IpFragmentOffset, @@ -47,6 +51,8 @@ impl core::fmt::Display for ValueType { match self { VlanId => write!(f, "VLAN ID"), VlanPcp => write!(f, "VLAN PCP (Priority Code Point)"), + MacsecAn => write!(f, "MACsec AN (Association Number)"), + MacsecShortLen => write!(f, "MACsec SL (Short Length)"), IpFragmentOffset => write!(f, "IP Fragment Offset"), Ipv4Dscp => write!(f, "IPv4 DSCP (Differentiated Services Code Point)"), Ipv4Ecn => write!(f, "IPv4 ECN (Explicit Congestion Notification)"), @@ -91,6 +97,8 @@ mod test { assert_eq!("VLAN ID", &format!("{}", VlanId)); assert_eq!("VLAN PCP (Priority Code Point)", &format!("{}", VlanPcp)); + assert_eq!("MACsec AN (Association Number)", &format!("{}", MacsecAn)); + assert_eq!("MACsec SL (Short Length)", &format!("{}", MacsecShortLen)); assert_eq!("IP Fragment Offset", &format!("{}", IpFragmentOffset)); assert_eq!( "IPv4 DSCP (Differentiated Services Code Point)", diff --git a/etherparse/src/lax_packet_headers.rs b/etherparse/src/lax_packet_headers.rs index b8a255f1..58f37938 100644 --- a/etherparse/src/lax_packet_headers.rs +++ b/etherparse/src/lax_packet_headers.rs @@ -2,6 +2,7 @@ use crate::{ err::{packet::SliceError, Layer, LenError}, *, }; +use arrayvec::ArrayVec; /// Decoded packet headers (data link layer and lower) with lax length checks. /// @@ -18,8 +19,8 @@ pub struct LaxPacketHeaders<'a> { /// Ethernet II header if present. pub link: Option, - /// Single or double vlan headers if present. - pub vlan: Option, + /// Link extension headers (VLAN & MAC Sec headers). + pub link_exts: ArrayVec, /// IPv4 or IPv6 header and IP extension headers if present. pub net: Option, @@ -35,6 +36,9 @@ pub struct LaxPacketHeaders<'a> { } impl<'a> LaxPacketHeaders<'a> { + /// Maximum supported number of link extensions headers. + pub const LINK_EXTS_CAP: usize = 3; + /// Separates a network packet into different headers from the ethernet header /// downwards with lax length checks and non-terminating errors. /// @@ -46,12 +50,12 @@ impl<'a> LaxPacketHeaders<'a> { /// # use etherparse::{Ethernet2Header, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac - /// # [7,8,9,10,11,12]) //destionation 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); //desitnation 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 @@ -76,8 +80,8 @@ impl<'a> LaxPacketHeaders<'a> { /// /// // parts that could be parsed without error /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// /// // net (ip) & transport (udp or tcp) @@ -86,6 +90,12 @@ impl<'a> LaxPacketHeaders<'a> { /// LaxPayloadSlice::Empty => { /// // in case of ARP packet the payload is empty /// } + /// LaxPayloadSlice::MacsecModified { payload, incomplete } => { + /// println!("MACsec modified payload: {:?}", payload); + /// if incomplete { + /// println!(" MACsec payload incomplete (length in MACsec header indicated more data should be present)"); + /// } + /// } /// LaxPayloadSlice::Ether(e) => { /// println!("ether payload (ether type {:?}): {:?}", e.ether_type, e.payload); /// } @@ -161,12 +171,12 @@ impl<'a> LaxPacketHeaders<'a> { /// # use etherparse::{Ethernet2Header, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac - /// # [7,8,9,10,11,12]) //destionation 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); //desitnation 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 @@ -190,18 +200,27 @@ impl<'a> LaxPacketHeaders<'a> { /// assert_eq!(value.link, None); /// /// // parts that could be parsed without error - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// /// // net (ip) & transport (udp or tcp) /// println!("net: {:?}", value.net); /// match value.payload { /// LaxPayloadSlice::Empty => { - /// // Some packets don't have seperate payloads. For example ARP packets. + /// // Some packets don't have separate payloads. For example ARP packets. /// } /// LaxPayloadSlice::Ether(e) => { /// println!("ether payload (ether type {:?}): {:?}", e.ether_type, e.payload); + /// if e.incomplete { + /// println!("ether payload incomplete (MACsec short length indicates more data should be present)"); + /// } + /// } + /// LaxPayloadSlice::MacsecModified{ payload, incomplete } => { + /// println!("MACsec modified payload: {:?}", payload); + /// if incomplete { + /// println!("MACsec payload (MACsec short length indicates more data should be present)"); + /// } /// } /// LaxPayloadSlice::Ip(ip) => { /// println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload); @@ -245,71 +264,113 @@ impl<'a> LaxPacketHeaders<'a> { let mut offset = 0; let mut result = LaxPacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, - payload: LaxPayloadSlice::Ether(EtherPayloadSlice { + payload: LaxPayloadSlice::Ether(LaxEtherPayloadSlice { + incomplete: false, ether_type, + len_source: LenSource::Slice, payload: rest, }), stop_err: None, }; // parse vlan header(s) + let mut len_source = LenSource::Slice; use ether_type::*; + loop { + match ether_type { + VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { + if result.link_exts.is_full() { + break; + } + let (vlan, vlan_rest) = match SingleVlanHeader::from_slice(rest) { + Ok(value) => value, + Err(err) => { + result.stop_err = Some(( + Len(err.add_offset(slice.len() - rest.len())), + Layer::VlanHeader, + )); + return result; + } + }; - 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 = vlan_rest; + offset += SingleVlanHeader::LEN; + ether_type = vlan.ether_type; + result.payload = LaxPayloadSlice::Ether(LaxEtherPayloadSlice { + incomplete: false, + ether_type, + len_source, + payload: rest, + }); + + // SAFETY: Safe as the while loop verified that there is still space left + unsafe { + result.link_exts.push_unchecked(LinkExtHeader::Vlan(vlan)); } - }; + } + MACSEC => { + use err::macsec::HeaderSliceError as H; - // 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, - }); + if result.link_exts.is_full() { + break; + } - // 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; - } - }; + let macsec = match LaxMacsecSlice::from_slice(rest) { + Ok(m) => m, + Err(err) => { + result.stop_err = Some(match err { + H::Content(c) => (Macsec(c), Layer::MacsecHeader), + H::Len(l) => ( + Len(l.add_offset(slice.len() - rest.len())), + Layer::MacsecHeader, + ), + }); + 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, - }); + // SAFETY: Safe as the while loop verified that there is still space left + unsafe { + result + .link_exts + .push_unchecked(LinkExtHeader::Macsec(macsec.header.to_header())); + } - Some(Double(DoubleVlanHeader { outer, inner })) + match macsec.payload { + LaxMacsecPayloadSlice::Unmodified(l) => { + rest = l.payload; + offset += macsec.header.header_len(); + ether_type = l.ether_type; + if l.len_source != LenSource::Slice { + len_source = l.len_source; + } + result.payload = LaxPayloadSlice::Ether(LaxEtherPayloadSlice { + incomplete: l.incomplete, + ether_type, + len_source, + payload: l.payload, + }); + } + LaxMacsecPayloadSlice::Modified { + incomplete, + payload, + } => { + result.payload = LaxPayloadSlice::MacsecModified { + incomplete, + payload, + }; + return result; + } } - // no second vlan header detected -> single vlan header - _ => Some(Single(outer)), } + // stop looping as soon as a non link extension ether type is encountered + _ => break, } - // no vlan header - _ => None, - }; + } // parse ip or arp match ether_type { @@ -374,7 +435,7 @@ impl<'a> LaxPacketHeaders<'a> { /// # [192,168,1,2], //destination ip /// # 20) //time to life /// # .udp(21, //source port - /// # 1234); //desitnation 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 @@ -401,7 +462,7 @@ impl<'a> LaxPacketHeaders<'a> { /// /// // link & vlan is unfilled /// assert_eq!(value.link, None); - /// assert_eq!(value.vlan, None); + /// assert!(value.link_exts.is_empty()); /// /// // parts that could be parsed without error /// println!("net: {:?}", value.net); @@ -412,7 +473,9 @@ impl<'a> LaxPacketHeaders<'a> { /// match value.payload { /// // if you parse from IP down there will be no ether payload and the /// // empty payload does not appear (only present in ARP packets). - /// LaxPayloadSlice::Ether(_) | LaxPayloadSlice::Empty => unreachable!(), + /// LaxPayloadSlice::Ether(_) | + /// LaxPayloadSlice::MacsecModified{ payload: _, incomplete: _} | + /// LaxPayloadSlice::Empty => unreachable!(), /// LaxPayloadSlice::Ip(ip) => { /// println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload); /// if ip.incomplete { @@ -454,10 +517,10 @@ impl<'a> LaxPacketHeaders<'a> { pub fn from_ip(slice: &'a [u8]) -> Result, err::ip::LaxHeaderSliceError> { let mut result = Self { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, - // dummy initialize (will be overwritten if add_ip is successfull) + // dummy initialize (will be overwritten if add_ip is successful) payload: LaxPayloadSlice::Udp { payload: &[], incomplete: true, @@ -468,6 +531,38 @@ impl<'a> LaxPacketHeaders<'a> { Ok(result) } + /// Returns the first two VLAN headers. + pub fn vlan(&self) -> Option { + let mut result = None; + for ext in &self.link_exts { + if let LinkExtHeader::Vlan(s) = ext { + if let Some(outer) = result { + return Some(VlanHeader::Double(DoubleVlanHeader { + outer, + inner: s.clone(), + })); + } else { + result = Some(s.clone()); + } + } + } + result.map(VlanHeader::Single) + } + + /// Returns the VLAN ids present in this packet. + pub fn vlan_ids(&self) -> ArrayVec { + let mut result = ArrayVec::::new_const(); + for e in &self.link_exts { + if let LinkExtHeader::Vlan(s) = e { + // SAFETY: Safe as the vlan ids array has the same size as slice.link_exts. + unsafe { + result.push_unchecked(s.vlan_id); + } + } + } + result + } + fn add_ip( &mut self, offset: usize, @@ -596,11 +691,13 @@ mod test { ether_type::VLAN_DOUBLE_TAGGED_FRAME, ]; + const MACSEC_ETHER_TYPES: [EtherType; 1] = [ether_type::MACSEC]; + #[test] fn clone_eq() { let header = LaxPacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, @@ -621,7 +718,7 @@ mod test { }; let header = LaxPacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, payload: payload.clone(), @@ -630,18 +727,212 @@ mod test { 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 + "LaxPacketHeaders {{ link: {:?}, link_exts: {:?}, net: {:?}, transport: {:?}, payload: {:?}, stop_err: {:?} }}", + header.link, header.link_exts, header.net, header.transport, payload, header.stop_err ) ); } + #[test] + fn vlan_vlan_ids() { + // no content + { + let headers = LaxPacketHeaders { + link: None, + link_exts: ArrayVec::new_const(), + net: None, + transport: None, + payload: LaxPayloadSlice::Empty, + stop_err: None, + }; + assert_eq!(headers.vlan(), None); + assert_eq!(headers.vlan_ids(), ArrayVec::::new_const()); + } + + // single vlan header + { + let outer = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let headers = LaxPacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Vlan(outer.clone())); + exts + }, + net: None, + transport: None, + payload: LaxPayloadSlice::Empty, + stop_err: None, + }; + + assert_eq!(headers.vlan(), Some(VlanHeader::Single(outer.clone()))); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids + }); + } + + // two vlan header + { + let outer = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + }; + let inner = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let headers = LaxPacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Vlan(outer.clone())); + exts.push(LinkExtHeader::Vlan(inner.clone())); + exts + }, + net: None, + transport: None, + payload: LaxPayloadSlice::Empty, + stop_err: None, + }; + + assert_eq!( + headers.vlan(), + Some(VlanHeader::Double(DoubleVlanHeader { + outer: outer.clone(), + inner: inner.clone(), + })) + ); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // two vlan header & macsec header + { + let macsec = MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType::VLAN_DOUBLE_TAGGED_FRAME), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }; + let outer = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + }; + let inner = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + + let headers = LaxPacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Macsec(macsec.clone())); + exts.push(LinkExtHeader::Vlan(outer.clone())); + exts.push(LinkExtHeader::Vlan(inner.clone())); + exts + }, + net: None, + transport: None, + payload: LaxPayloadSlice::Empty, + stop_err: None, + }; + + assert_eq!( + headers.vlan(), + Some(VlanHeader::Double(DoubleVlanHeader { + outer: outer.clone(), + inner: inner.clone(), + })) + ); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // three vlan header + { + let vlan1 = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + }; + let vlan2 = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let vlan3 = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(3).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let headers = LaxPacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Vlan(vlan1.clone())); + exts.push(LinkExtHeader::Vlan(vlan2.clone())); + exts.push(LinkExtHeader::Vlan(vlan3.clone())); + exts + }, + net: None, + transport: None, + payload: LaxPayloadSlice::Empty, + stop_err: None, + }; + + assert_eq!( + headers.vlan(), + Some(VlanHeader::Double(DoubleVlanHeader { + outer: vlan1.clone(), + inner: vlan2.clone(), + })) + ); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids.push(VlanId::try_new(3).unwrap()); + ids + }); + } + } + #[test] fn from_x_slice() { // no eth - from_x_slice_vlan_variants(&TestPacket { + from_x_slice_link_exts_variants(&TestPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }); @@ -655,13 +946,13 @@ mod test { }; let test = TestPacket { link: Some(LinkHeader::Ethernet2(eth.clone())), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; // ok ethernet header (with unknown next) - from_x_slice_vlan_variants(&test); + from_x_slice_link_exts_variants(&test); // eth len error { @@ -677,13 +968,15 @@ mod test { 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!(actual.link_exts.is_empty()); assert_eq!(None, actual.net); assert_eq!(None, actual.transport); assert_eq!( actual.payload, - LaxPayloadSlice::Ether(EtherPayloadSlice { + LaxPayloadSlice::Ether(LaxEtherPayloadSlice { + incomplete: false, ether_type: 0.into(), + len_source: LenSource::Slice, payload: &payload }) ); @@ -691,96 +984,183 @@ mod test { } } - fn from_x_slice_vlan_variants(base: &TestPacket) { - // none - from_x_slice_net_variants(base); + fn from_x_slice_link_exts_variants(base: &TestPacket) { + #[derive(Copy, Clone, Eq, PartialEq)] + enum Ext { + Macsec, + VlanTaggedFrame, + VlanDoubleTaggedFrame, + ProviderBridging, + } - // 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(), - }; + impl Ext { + pub fn ether_type(&self) -> EtherType { + match self { + Ext::Macsec => EtherType::MACSEC, + Ext::VlanTaggedFrame => EtherType::VLAN_TAGGED_FRAME, + Ext::VlanDoubleTaggedFrame => EtherType::VLAN_DOUBLE_TAGGED_FRAME, + Ext::ProviderBridging => EtherType::PROVIDER_BRIDGING, + } + } - for vlan_ether_type in VLAN_ETHER_TYPES { + pub fn add(&self, base: &TestPacket) -> TestPacket { let mut test = base.clone(); - test.set_ether_type(vlan_ether_type); - test.vlan = Some(VlanHeader::Single(single.clone())); + test.set_ether_type(self.ether_type()); + test.link_exts + .try_push(match self { + Ext::Macsec => LinkExtHeader::Macsec(MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType(3)), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }), + Ext::VlanTaggedFrame + | Ext::VlanDoubleTaggedFrame + | Ext::ProviderBridging => LinkExtHeader::Vlan(SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: 3.into(), + }), + }) + .unwrap(); + test + } + } - // ok vlan header - from_x_slice_net_variants(&test); + let test_macsec_mod = |test: &TestPacket| { + for ptype in [ + MacsecPType::Modified, + MacsecPType::Encrypted, + MacsecPType::EncryptedUnmodified, + ] { + let mut test = test.clone(); + if let Some(LinkExtHeader::Macsec(m)) = test.link_exts.last_mut() { + m.ptype = ptype; + } + if matches!(test.link_exts.last(), Some(LinkExtHeader::Macsec(_))) { + from_x_slice_assert_ok(&test); + } + } + }; - // len error - { - let data = test.to_vec(&[]); - for len in 0..single.header_len() { - let base_len = test.len(&[]) - single.header_len(); + let len_errors = |test: &TestPacket| { + let data = test.to_vec(&[]); + let req_len = test.link_exts.last().unwrap().header_len(); + for len in 0..req_len { + let base_len = test.len(&[]) - req_len; + + let (err_req_len, err_layer) = match test.link_exts.last().unwrap() { + LinkExtHeader::Vlan(h) => (h.header_len(), Layer::VlanHeader), + LinkExtHeader::Macsec(_) => { + if len < 6 { + (6, Layer::MacsecHeader) + } else { + (req_len, Layer::MacsecHeader) + } + } + }; - 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)), - ); + let mut len_source = LenSource::Slice; + for prev_exts in test.link_exts.iter().rev().skip(1) { + if let LinkExtHeader::Macsec(m) = prev_exts { + if m.short_len != MacsecShortLen::ZERO { + len_source = LenSource::MacsecShortLength; + } } } + + let err = LenError { + required_len: err_req_len, + len, + len_source, + layer: err_layer, + layer_start_offset: base_len, + }; + assert_test_result( + &test, + &[], + &data[..base_len + len], + None, + Some((SliceError::Len(err.clone()), err_layer)), + ); } - } + }; - // 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 content_errors = |test: &TestPacket| { + if let Some(LinkExtHeader::Macsec(last)) = test.link_exts.last() { + let mut data = test.to_vec(&[]); + + // inject bad version id + let macsec_offset = data.len() - last.header_len(); + data[macsec_offset] = data[macsec_offset] | 0b1000_0000; + + assert_test_result( + &{ + let mut expected = test.clone(); + expected.link_exts.pop(); + expected }, - }; - let mut test = base.clone(); - test.set_ether_type(outer_vlan_ether_type); - test.vlan = Some(VlanHeader::Double(double.clone())); + &[], + &data, + None, + Some(( + SliceError::Macsec(err::macsec::HeaderError::UnexpectedVersion), + Layer::MacsecHeader, + )), + ); + } + }; - // ok double vlan header - from_x_slice_net_variants(&test); + // extensions + let extensions = [ + Ext::Macsec, + Ext::VlanTaggedFrame, + Ext::VlanDoubleTaggedFrame, + Ext::ProviderBridging, + ]; - // len error - { - let data = test.to_vec(&[]); - for len in 0..SingleVlanHeader::LEN { - let base_len = test.len(&[]) - SingleVlanHeader::LEN; + // none + from_x_slice_net_variants(base); - 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)), - ); + // add up to three layers of extensions + for ext0 in extensions { + let test0 = ext0.add(base); + from_x_slice_net_variants(&test0); + test_macsec_mod(&test0); + len_errors(&test0); + content_errors(&test0); + + for ext1 in extensions { + let test1 = ext1.add(&test0); + from_x_slice_net_variants(&test1); + test_macsec_mod(&test1); + len_errors(&test1); + content_errors(&test1); + + for ext2 in extensions { + let test2 = ext2.add(&test1); + from_x_slice_net_variants(&test2); + test_macsec_mod(&test2); + len_errors(&test2); + content_errors(&test2); + + // above max supported link ext + for ext3 in extensions { + let mut test3 = test2.clone(); + let l = test3.link_exts.last_mut().unwrap(); + match l { + LinkExtHeader::Vlan(s) => { + s.ether_type = ext3.ether_type(); + } + LinkExtHeader::Macsec(m) => { + m.ptype = MacsecPType::Unmodified(ext3.ether_type()); + } + } + from_x_slice_assert_ok(&test3); } } } @@ -954,9 +1334,7 @@ mod test { 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), - ); + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); let data = test.to_vec(&[]); let base_len = test.len(&[]) - auth.header_len(); @@ -1120,9 +1498,7 @@ mod test { 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), - ); + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); let data = test.to_vec(&[]); let base_len = test.len(&[]) - auth.header_len(); @@ -1224,7 +1600,7 @@ mod test { let mut test = test.clone(); // set payload length - test.set_payload_le_from_ip_on(len as isize); + test.set_payload_len_ip(len as isize); // generate data let data = test.to_vec(&[]); @@ -1278,7 +1654,7 @@ mod test { 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); + test.set_payload_len_ip(len as isize); let data = test.to_vec(&[]); let base_len = test.len(&[]) - (tcp.header_len() as usize); @@ -1349,7 +1725,7 @@ mod test { 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); + test.set_payload_len_ip(len as isize); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv4.header_len(); @@ -1401,7 +1777,7 @@ mod test { 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); + test.set_payload_len_ip(len as isize); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv6.header_len(); @@ -1455,33 +1831,37 @@ mod test { expected_ip_err: Option, 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() { + fn compare_exts(test: &TestPacket, data: &[u8], actual: &LaxPacketHeaders) { + let mut 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); + + let mut expected = ArrayVec::::new(); + for e in &test.link_exts { + match e { + LinkExtHeader::Vlan(s) => { + if data.len() >= vlan_offset + s.header_len() { + expected.push(e.clone()); + vlan_offset += s.header_len(); + } else { + // no more space left + break; + } } - } - 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); + LinkExtHeader::Macsec(m) => { + if data.len() >= vlan_offset + m.header_len() { + expected.push(e.clone()); + vlan_offset += m.header_len(); + } else { + // no more space left + break; + } } } - None => { - assert_eq!(None, actual.vlan); - } } + assert_eq!(expected, actual.link_exts); } fn compare_net_only(test: &TestPacket, actual: &LaxPacketHeaders) { @@ -1580,7 +1960,7 @@ mod test { match expected_stop_err.as_ref().map(|v| v.1) { None => { assert_eq!(test.link, actual.link); - compare_vlan(test, data, &actual); + compare_exts(test, data, &actual); assert_eq!(test.net, actual.net); compare_transport( test, @@ -1591,7 +1971,14 @@ mod test { } Some(Layer::VlanHeader) => { assert_eq!(test.link, actual.link); - compare_vlan(test, data, &actual); + compare_exts(test, data, &actual); + assert_eq!(None, actual.net); + assert_eq!(None, actual.transport); + assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); + } + Some(Layer::MacsecHeader) => { + assert_eq!(test.link, actual.link); + compare_exts(test, data, &actual); assert_eq!(None, actual.net); assert_eq!(None, actual.transport); assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); @@ -1601,7 +1988,7 @@ mod test { | Some(Layer::IpHeader) | Some(Layer::Arp) => { assert_eq!(test.link, actual.link); - compare_vlan(test, data, &actual); + compare_exts(test, data, &actual); assert_eq!(None, actual.net); assert_eq!(None, actual.transport); assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); @@ -1613,7 +2000,7 @@ mod test { | Some(Layer::Ipv6RouteHeader) | Some(Layer::Ipv6FragHeader) => { assert_eq!(test.link, actual.link); - compare_vlan(test, data, &actual); + compare_exts(test, data, &actual); compare_net_only(test, &actual); assert_eq!(None, actual.transport); assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); @@ -1623,7 +2010,7 @@ mod test { | Some(Layer::Icmpv4) | Some(Layer::Icmpv6) => { assert_eq!(test.link, actual.link); - compare_vlan(test, data, &actual); + compare_exts(test, data, &actual); assert_eq!(test.net, actual.net); assert_eq!(None, actual.transport); assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_))); @@ -1633,11 +2020,15 @@ mod test { } } // 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); + if test.link.is_none() && !test.link_exts.is_empty() { + let ether_types: &[EtherType] = match test.link_exts.first().unwrap() { + LinkExtHeader::Vlan(_) => &VLAN_ETHER_TYPES, + LinkExtHeader::Macsec(_) => &MACSEC_ETHER_TYPES, + }; + for ether_type in ether_types { + let actual = LaxPacketHeaders::from_ether_type(*ether_type, data); assert_eq!(actual.stop_err, expected_stop_err); - compare_vlan(test, data, &actual); + compare_exts(test, data, &actual); match expected_stop_err.as_ref().map(|v| v.1) { None => { assert_eq!(test.net, actual.net); @@ -1653,6 +2044,11 @@ mod test { assert_eq!(None, actual.transport); assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_))); } + Some(Layer::MacsecHeader) => { + 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) @@ -1684,7 +2080,7 @@ mod test { } } // from_ether_type (ip or arp at start) - if test.link.is_none() && test.vlan.is_none() { + if test.link.is_none() && test.link_exts.is_empty() { if let Some(net) = &test.net { let ether_type = match net { NetHeaders::Ipv4(_, _) => ether_type::IPV4, @@ -1694,7 +2090,7 @@ mod test { let actual = LaxPacketHeaders::from_ether_type(ether_type, &data); assert_eq!(actual.stop_err, expected_stop_err); assert_eq!(None, actual.link); - assert_eq!(None, test.vlan); + assert!(actual.link_exts.is_empty()); match expected_stop_err.as_ref().map(|v| v.1) { None => { assert_eq!(test.net, actual.net); @@ -1712,8 +2108,10 @@ mod test { assert_eq!(None, actual.net); assert_eq!(None, actual.transport); assert_eq!( - LaxPayloadSlice::Ether(EtherPayloadSlice { + LaxPayloadSlice::Ether(LaxEtherPayloadSlice { + incomplete: false, ether_type, + len_source: LenSource::Slice, payload: data }), actual.payload @@ -1743,7 +2141,7 @@ mod test { } // from_ip_slice if test.link.is_none() - && test.vlan.is_none() + && test.link_exts.is_empty() && test.net.is_some() && !matches!(test.net, Some(NetHeaders::Arp(_))) { @@ -1753,7 +2151,7 @@ mod test { 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); + assert!(actual.link_exts.is_empty()); match expected_stop_err.as_ref().map(|v| v.1) { None => { assert_eq!(test.net, actual.net); diff --git a/etherparse/src/lax_payload_slice.rs b/etherparse/src/lax_payload_slice.rs index e1934bd0..485604fd 100644 --- a/etherparse/src/lax_payload_slice.rs +++ b/etherparse/src/lax_payload_slice.rs @@ -6,20 +6,36 @@ use crate::*; pub enum LaxPayloadSlice<'a> { /// No specific payload (e.g. ARP packet). Empty, + /// Payload with it's type identified by an ether type number /// (e.g. after an ethernet II or vlan header). - Ether(EtherPayloadSlice<'a>), + Ether(LaxEtherPayloadSlice<'a>), + + /// MACsec modified payload (either by encryption or other algorithm). + MacsecModified { + payload: &'a [u8], + /// True if the payload has been cut off. + incomplete: bool, + }, + /// Payload with is's type identified by an ip number (e.g. /// after an IP header or after an) Ip(LaxIpPayloadSlice<'a>), + /// UDP payload. - Udp { payload: &'a [u8], incomplete: bool }, + Udp { + payload: &'a [u8], + /// True if the payload has been cut off. + incomplete: bool, + }, + /// TCP payload. Tcp { payload: &'a [u8], /// True if the payload has been cut off. incomplete: bool, }, + /// Payload part of an ICMP V4 message. Check [`crate::Icmpv4Type`] /// for a description what will be part of the payload. Icmpv4 { @@ -27,6 +43,7 @@ pub enum LaxPayloadSlice<'a> { /// True if the payload has been cut off. incomplete: bool, }, + /// Payload part of an ICMP V4 message. Check [`crate::Icmpv6Type`] /// for a description what will be part of the payload. Icmpv6 { @@ -41,6 +58,10 @@ impl<'a> LaxPayloadSlice<'a> { match self { LaxPayloadSlice::Empty => &[], LaxPayloadSlice::Ether(e) => e.payload, + LaxPayloadSlice::MacsecModified { + payload, + incomplete: _, + } => payload, LaxPayloadSlice::Ip(i) => i.payload, LaxPayloadSlice::Udp { payload, @@ -115,13 +136,23 @@ mod test { use LaxPayloadSlice::*; assert_eq!( - Ether(EtherPayloadSlice { + Ether(LaxEtherPayloadSlice { + incomplete: false, ether_type: EtherType::IPV4, - payload: &payload + len_source: LenSource::Slice, + payload: &payload, }) .slice(), &payload ); + assert_eq!( + MacsecModified { + payload: &payload, + incomplete: false, + } + .slice(), + &payload + ); assert_eq!( Ip(LaxIpPayloadSlice { ip_number: IpNumber::IPV4, diff --git a/etherparse/src/lax_sliced_packet.rs b/etherparse/src/lax_sliced_packet.rs index d790529c..cea10b7e 100644 --- a/etherparse/src/lax_sliced_packet.rs +++ b/etherparse/src/lax_sliced_packet.rs @@ -1,3 +1,5 @@ +use arrayvec::ArrayVec; + use crate::{err::Layer, *}; /// Packet slice split into multiple slices containing @@ -7,8 +9,8 @@ pub struct LaxSlicedPacket<'a> { /// Ethernet II header if present. pub link: Option>, - /// Single or double vlan headers if present. - pub vlan: Option>, + /// Link extensions (VLAN & MAC Sec headers). + pub link_exts: ArrayVec, { LaxSlicedPacket::LINK_EXTS_CAP }>, /// IPv4 or IPv6 header, IP extension headers & payload if present. pub net: Option>, @@ -21,6 +23,9 @@ pub struct LaxSlicedPacket<'a> { } impl<'a> LaxSlicedPacket<'a> { + /// Maximum supported number of link extensions. + pub const LINK_EXTS_CAP: usize = 3; + /// Separates a network packet slice into different slices containing the /// headers from the ethernet header downwards with lax length checks and /// non-terminating errors. @@ -33,12 +38,12 @@ impl<'a> LaxSlicedPacket<'a> { /// # use etherparse::{Ethernet2Header, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac - /// # [7,8,9,10,11,12]) //destionation 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); //desitnation 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 @@ -63,11 +68,9 @@ impl<'a> LaxSlicedPacket<'a> { /// /// // parts that could be parsed without error /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); - /// println!("transport: {:?}", value.transport); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec /// - /// // net (ip) & transport (udp or tcp) + /// // net (ip or arp) /// println!("net: {:?}", value.net); /// if let Some(ip_payload) = value.net.as_ref().map(|net| net.ip_payload_ref()).flatten() { /// // the ip payload len_source field can be used to check @@ -75,12 +78,14 @@ impl<'a> LaxSlicedPacket<'a> { /// if ip_payload.len_source == LenSource::Slice { /// println!(" Used slice length as fallback to identify the IP payload"); /// } else { - /// println!(" IP payload could correctly be identfied via the length field in the header"); + /// println!(" IP payload could correctly be identified via the length field in the header"); /// } /// } + /// + /// // transport (udp or tcp) /// println!("transport: {:?}", value.transport); /// } - /// } + /// }; /// /// ``` pub fn from_ethernet(slice: &'a [u8]) -> Result, err::LenError> { @@ -111,12 +116,12 @@ impl<'a> LaxSlicedPacket<'a> { /// # use etherparse::{Ethernet2Header, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac - /// # [7,8,9,10,11,12]) //destionation 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); //desitnation 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 @@ -138,8 +143,8 @@ impl<'a> LaxSlicedPacket<'a> { /// /// // parts that could be parsed without error /// println!("link: {:?}", packet.link); - /// println!("vlan: {:?}", packet.vlan); - /// println!("net: {:?}", packet.net); + /// println!("link_exts: {:?}", packet.link_exts); // vlan & macsec + /// println!("net: {:?}", packet.net); // ip & arp /// println!("transport: {:?}", packet.transport); /// /// ``` @@ -177,7 +182,7 @@ impl<'a> LaxSlicedPacket<'a> { /// # [192,168,1,2], //destination ip /// # 20) //time to life /// # .udp(21, //source port - /// # 1234); //desitnation 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 @@ -200,7 +205,7 @@ impl<'a> LaxSlicedPacket<'a> { /// /// // link & vlan fields are empty when parsing from ip downwards /// assert_eq!(None, value.link); - /// assert_eq!(None, value.vlan); + /// assert!(value.link_exts.is_empty()); /// /// // net (ip) & transport (udp or tcp) /// println!("net: {:?}", value.net); @@ -210,43 +215,115 @@ impl<'a> LaxSlicedPacket<'a> { /// if ip_payload.len_source == LenSource::Slice { /// println!(" Used slice length as fallback to identify the IP payload"); /// } else { - /// println!(" IP payload could correctly be identfied via the length field in the header"); + /// println!(" IP payload could correctly be identified via the length field in the header"); /// } /// } /// println!("transport: {:?}", value.transport); /// } - /// } + /// }; /// ``` pub fn from_ip(slice: &'a [u8]) -> Result, err::ip::LaxHeaderSliceError> { LaxSlicedPacketCursor::parse_from_ip(slice) } + /// Single or double vlan headers if present. + pub fn vlan(&self) -> Option> { + let mut result = None; + for ext in &self.link_exts { + if let LaxLinkExtSlice::Vlan(s) = ext { + if let Some(outer) = result { + return Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer, + inner: s.clone(), + })); + } else { + result = Some(s.clone()); + } + } + } + result.map(VlanSlice::SingleVlan) + } + + /// Returns the VLAN ids present in this packet. + pub fn vlan_ids(&self) -> ArrayVec { + let mut result = ArrayVec::::new_const(); + for e in &self.link_exts { + if let LaxLinkExtSlice::Vlan(s) = e { + // SAFETY: Safe as the vlan ids array has the same size as slice.link_exts. + unsafe { + result.push_unchecked(s.vlan_identifier()); + } + } + } + result + } + /// 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> { - if let Some(vlan) = self.vlan.as_ref() { - match vlan { - VlanSlice::SingleVlan(s) => Some(s.payload()), - VlanSlice::DoubleVlan(s) => Some(s.payload()), + pub fn ether_payload(&self) -> Option> { + if let Some(link_ext) = self.link_exts.last() { + match link_ext { + LaxLinkExtSlice::Vlan(vlan_slice) => Some(LaxEtherPayloadSlice { + incomplete: false, + ether_type: vlan_slice.ether_type(), + len_source: { + let mut len_source = LenSource::Slice; + for ext in &self.link_exts { + if let Some(l) = ext.payload().as_ref() { + if l.len_source != LenSource::Slice { + len_source = l.len_source; + } + } + } + len_source + }, + payload: vlan_slice.payload_slice(), + }), + LaxLinkExtSlice::Macsec(macsec_slice) => macsec_slice.ether_payload(), } } else if let Some(link) = self.link.as_ref() { match link { - LinkSlice::Ethernet2(e) => Some(e.payload()), + LinkSlice::Ethernet2(e) => { + let p = e.payload(); + Some(LaxEtherPayloadSlice { + incomplete: false, + ether_type: p.ether_type, + len_source: LenSource::Slice, + payload: p.payload, + }) + } LinkSlice::LinuxSll(e) => match e.protocol_type() { LinuxSllProtocolType::EtherType(_) | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => { - Some(EtherPayloadSlice::try_from(e.payload()).ok()?) + let p = EtherPayloadSlice::try_from(e.payload()).ok()?; + Some(LaxEtherPayloadSlice { + incomplete: false, + ether_type: p.ether_type, + len_source: LenSource::Slice, + payload: p.payload, + }) } _ => None, }, - LinkSlice::EtherPayload(e) => Some(e.clone()), + LinkSlice::EtherPayload(p) => Some(LaxEtherPayloadSlice { + incomplete: false, + ether_type: p.ether_type, + len_source: LenSource::Slice, + payload: p.payload, + }), LinkSlice::LinuxSllPayload(e) => match e.protocol_type { LinuxSllProtocolType::EtherType(_) | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => { - Some(EtherPayloadSlice::try_from(e.clone()).ok()?) + let p = EtherPayloadSlice::try_from(e.clone()).ok()?; + Some(LaxEtherPayloadSlice { + incomplete: false, + ether_type: p.ether_type, + len_source: LenSource::Slice, + payload: p.payload, + }) } _ => None, }, @@ -274,6 +351,8 @@ impl<'a> LaxSlicedPacket<'a> { #[cfg(test)] mod test { + use std::vec::Vec; + use super::*; use crate::err::{packet::SliceError, LenError}; use crate::test_packet::TestPacket; @@ -283,12 +362,13 @@ mod test { ether_type::PROVIDER_BRIDGING, ether_type::VLAN_DOUBLE_TAGGED_FRAME, ]; + const MACSEC_ETHER_TYPES: [EtherType; 1] = [ether_type::MACSEC]; #[test] fn clone_eq() { let header = LaxSlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, @@ -301,7 +381,7 @@ mod test { use alloc::format; let header = LaxSlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, @@ -309,12 +389,221 @@ mod test { assert_eq!( format!("{:?}", header), format!( - "LaxSlicedPacket {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?}, stop_err: {:?} }}", - header.link, header.vlan, header.net, header.transport, header.stop_err + "LaxSlicedPacket {{ link: {:?}, link_exts: {:?}, net: {:?}, transport: {:?}, stop_err: {:?} }}", + header.link, header.link_exts, header.net, header.transport, header.stop_err ) ); } + #[test] + fn vlan_vlan_ids() { + // no content + assert_eq!( + LaxSlicedPacket { + link: None, + link_exts: ArrayVec::new_const(), + net: None, + transport: None, + stop_err: None + } + .vlan(), + None + ); + assert_eq!( + LaxSlicedPacket { + link: None, + link_exts: ArrayVec::new_const(), + net: None, + transport: None, + stop_err: None + } + .vlan_ids(), + ArrayVec::::new_const() + ); + + // single vlan header + { + let payload = [1, 2, 3, 4]; + let mut buf = Vec::with_capacity(SingleVlanHeader::LEN + 4); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = LaxSlicedPacket::from_ether_type(ether_type::VLAN_TAGGED_FRAME, &buf); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::SingleVlan(SingleVlanSlice { slice: &buf[..] })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids + }); + } + + // two vlan header + { + let payload = [1, 2, 3, 4]; + let mut buf = Vec::with_capacity(SingleVlanHeader::LEN * 2 + 4); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = + LaxSlicedPacket::from_ether_type(ether_type::VLAN_DOUBLE_TAGGED_FRAME, &buf); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer: SingleVlanSlice { slice: &buf }, + inner: SingleVlanSlice { + slice: &buf[SingleVlanHeader::LEN..] + }, + })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // two vlan header with macsec + { + let payload = [1, 2, 3, 4]; + let macsec = MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType::VLAN_DOUBLE_TAGGED_FRAME), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }; + let mut buf = Vec::with_capacity(macsec.header_len() + SingleVlanHeader::LEN * 2 + 4); + buf.extend_from_slice(&macsec.to_bytes()); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = LaxSlicedPacket::from_ether_type(ether_type::MACSEC, &buf); + + let vlan_start = macsec.header_len(); + assert_eq!( + slice.vlan(), + Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer: SingleVlanSlice { + slice: &buf[vlan_start..] + }, + inner: SingleVlanSlice { + slice: &buf[vlan_start + SingleVlanHeader::LEN..] + }, + })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // three vlan header + { + let payload = [1, 2, 3, 4]; + let mut buf = Vec::with_capacity(SingleVlanHeader::LEN * 3 + 4); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(3).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = + LaxSlicedPacket::from_ether_type(ether_type::VLAN_DOUBLE_TAGGED_FRAME, &buf); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer: SingleVlanSlice { slice: &buf }, + inner: SingleVlanSlice { + slice: &buf[SingleVlanHeader::LEN..] + }, + })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids.push(VlanId::try_new(3).unwrap()); + ids + }); + } + } + #[test] fn ether_payload() { use alloc::vec::*; @@ -323,7 +612,7 @@ mod test { assert_eq!( LaxSlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None @@ -348,8 +637,10 @@ mod test { LaxSlicedPacket::from_ethernet(&buf) .unwrap() .ether_payload(), - Some(EtherPayloadSlice { + Some(LaxEtherPayloadSlice { + incomplete: false, ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -362,16 +653,19 @@ mod test { LaxSlicedPacket { link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload })), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, } .ether_payload(), - Some(EtherPayloadSlice { + Some(LaxEtherPayloadSlice { + incomplete: false, ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -400,8 +694,10 @@ mod test { LaxSlicedPacket::from_ethernet(&buf) .unwrap() .ether_payload(), - Some(EtherPayloadSlice { + Some(LaxEtherPayloadSlice { + incomplete: false, ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -437,8 +733,10 @@ mod test { LaxSlicedPacket::from_ethernet(&buf) .unwrap() .ether_payload(), - Some(EtherPayloadSlice { + Some(LaxEtherPayloadSlice { + incomplete: false, ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -453,7 +751,7 @@ mod test { assert_eq!( LaxSlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, @@ -516,9 +814,9 @@ mod test { #[test] fn from_x_slice() { // no eth - from_x_slice_vlan_variants(&TestPacket { + from_x_slice_link_exts_variants(&TestPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }); @@ -532,13 +830,13 @@ mod test { }; let test = TestPacket { link: Some(LinkHeader::Ethernet2(eth.clone())), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; // ok ethernet header (with unknown next) - from_x_slice_vlan_variants(&test); + from_x_slice_link_exts_variants(&test); // eth len error { @@ -557,113 +855,182 @@ mod test { actual.link, Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: 0.into(), + len_source: LenSource::Slice, payload: &payload })) ); - assert_eq!(None, actual.vlan); + assert!(actual.link_exts.is_empty()); assert_eq!(None, actual.net); assert_eq!(None, actual.transport); assert_eq!(None, actual.stop_err); } } - fn from_x_slice_vlan_variants(base: &TestPacket) { - // none - from_x_slice_ip_variants(base); + fn from_x_slice_link_exts_variants(base: &TestPacket) { + #[derive(Copy, Clone, Eq, PartialEq)] + enum Ext { + Macsec, + VlanTaggedFrame, + VlanDoubleTaggedFrame, + ProviderBridging, + } - // 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(), - }; + impl Ext { + pub fn ether_type(&self) -> EtherType { + match self { + Ext::Macsec => EtherType::MACSEC, + Ext::VlanTaggedFrame => EtherType::VLAN_TAGGED_FRAME, + Ext::VlanDoubleTaggedFrame => EtherType::VLAN_DOUBLE_TAGGED_FRAME, + Ext::ProviderBridging => EtherType::PROVIDER_BRIDGING, + } + } - for vlan_ether_type in VLAN_ETHER_TYPES { + pub fn add(&self, base: &TestPacket) -> TestPacket { 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); + test.set_ether_type(self.ether_type()); + test.link_exts + .try_push(match self { + Ext::Macsec => LinkExtHeader::Macsec(MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType(3)), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }), + Ext::VlanTaggedFrame + | Ext::VlanDoubleTaggedFrame + | Ext::ProviderBridging => LinkExtHeader::Vlan(SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: 3.into(), + }), + }) + .unwrap(); + test + } + } - // len error - { - let data = test.to_vec(&[]); - for len in 0..single.header_len() { - let base_len = test.len(&[]) - single.header_len(); + let len_errors = |test: &TestPacket| { + let data = test.to_vec(&[]); + let req_len = test.link_exts.last().unwrap().header_len(); + for len in 0..req_len { + let base_len = test.len(&[]) - req_len; + + let (err_req_len, err_layer) = match test.link_exts.last().unwrap() { + LinkExtHeader::Vlan(h) => (h.header_len(), Layer::VlanHeader), + LinkExtHeader::Macsec(_) => { + if len < 6 { + (6, Layer::MacsecHeader) + } else { + (req_len, Layer::MacsecHeader) + } + } + }; - 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)), - ); + let mut len_source = LenSource::Slice; + for prev_exts in test.link_exts.iter().rev().skip(1) { + if let LinkExtHeader::Macsec(m) = prev_exts { + if m.short_len != MacsecShortLen::ZERO { + len_source = LenSource::MacsecShortLength; + } } } - } - } - // 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 err = LenError { + required_len: err_req_len, + len, + len_source, + layer: err_layer, + layer_start_offset: base_len, }; - let mut test = base.clone(); - test.set_ether_type(outer_vlan_ether_type); - test.vlan = Some(VlanHeader::Double(double.clone())); + assert_test_result( + &test, + &[], + &data[..base_len + len], + None, + Some((SliceError::Len(err.clone()), err_layer)), + ); + } + }; - // ok double vlan header - from_x_slice_ip_variants(&test); + let content_errors = |test: &TestPacket| { + if let Some(LinkExtHeader::Macsec(last)) = test.link_exts.last() { + let mut data = test.to_vec(&[]); - // len error - { - let data = test.to_vec(&[]); - for len in 0..SingleVlanHeader::LEN { - let base_len = test.len(&[]) - SingleVlanHeader::LEN; + // inject bad version id + let macsec_offset = data.len() - last.header_len(); + data[macsec_offset] = data[macsec_offset] | 0b1000_0000; - 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)), - ); + assert_test_result( + &{ + let mut expected = test.clone(); + expected.link_exts.pop(); + expected + }, + &[], + &data, + None, + Some(( + SliceError::Macsec(err::macsec::HeaderError::UnexpectedVersion), + Layer::MacsecHeader, + )), + ); + } + }; + + // extensions + let extensions = [ + Ext::Macsec, + Ext::VlanTaggedFrame, + Ext::VlanDoubleTaggedFrame, + Ext::ProviderBridging, + ]; + + // none + from_x_slice_net_variants(base); + + // add up to three layers of extensions + for ext0 in extensions { + let test0 = ext0.add(base); + from_x_slice_net_variants(&test0); + len_errors(&test0); + content_errors(&test0); + + for ext1 in extensions { + let test1 = ext1.add(&test0); + from_x_slice_net_variants(&test1); + len_errors(&test1); + content_errors(&test1); + + for ext2 in extensions { + let test2 = ext2.add(&test1); + from_x_slice_net_variants(&test2); + len_errors(&test2); + content_errors(&test2); + + // above max supported link ext + for ext3 in extensions { + let mut test3 = test2.clone(); + let l = test3.link_exts.last_mut().unwrap(); + match l { + LinkExtHeader::Vlan(s) => { + s.ether_type = ext3.ether_type(); + } + LinkExtHeader::Macsec(m) => { + m.ptype = MacsecPType::Unmodified(ext3.ether_type()); + } + } + from_x_slice_assert_ok(&test3); } } } } } - fn from_x_slice_ip_variants(base: &TestPacket) { + fn from_x_slice_net_variants(base: &TestPacket) { // none from_x_slice_transport_variants(base); @@ -783,9 +1150,7 @@ mod test { 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), - ); + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); let data = test.to_vec(&[]); let base_len = test.len(&[]) - auth.header_len(); @@ -949,9 +1314,7 @@ mod test { 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), - ); + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); let data = test.to_vec(&[]); let base_len = test.len(&[]) - auth.header_len(); @@ -1014,6 +1377,68 @@ mod test { } } } + + // arp + { + let arp = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0u8; 6], + &[0u8; 4], + &[0u8; 6], + &[0u8; 4], + ) + .unwrap(); + + let mut test = base.clone(); + test.set_ether_type(ether_type::ARP); + test.net = Some(NetHeaders::Arp(arp.clone())); + test.set_payload_len(0); + + // ok arp + from_x_slice_assert_ok(&test); + + // len error + { + let data = test.to_vec(&[]); + for len in 0..arp.packet_len() { + let base_len = test.len(&[]) - arp.packet_len(); + + let err = err::LenError { + required_len: if len < 8 { 8 } else { arp.packet_len() }, + len, + len_source: if len < 8 { + LenSource::Slice + } else { + LenSource::ArpAddrLengths + }, + layer: Layer::Arp, + 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 < 8 { + let mut err = err.clone(); + err.required_len = 8; + err.layer = Layer::Arp; + err + } else { + err.clone() + } + }), + Layer::Arp, + )), + ); + } + } + } } fn from_x_slice_transport_variants(base: &TestPacket) { @@ -1053,7 +1478,7 @@ mod test { let mut test = test.clone(); // set payload length - test.set_payload_le_from_ip_on(len as isize); + test.set_payload_len_ip(len as isize); // generate data let data = test.to_vec(&[]); @@ -1107,7 +1532,7 @@ mod test { 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); + test.set_payload_len_ip(len as isize); let data = test.to_vec(&[]); let base_len = test.len(&[]) - (tcp.header_len() as usize); @@ -1178,7 +1603,7 @@ mod test { 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); + test.set_payload_len_ip(len as isize); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv4.header_len(); @@ -1230,7 +1655,7 @@ mod test { 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); + test.set_payload_len_ip(len as isize); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv6.header_len(); @@ -1284,39 +1709,42 @@ mod test { expected_ip_err: Option, expected_stop_err: Option<(SliceError, Layer)>, ) { - fn compare_vlan(test: &TestPacket, data: &[u8], actual: &LaxSlicedPacket) { - let vlan_offset = if let Some(e) = test.link.as_ref() { + fn compare_link_exts(test: &TestPacket, data: &[u8], actual: &LaxSlicedPacket) { + let mut next_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.as_ref().map(|v| v.to_header())); - } else if data.len() >= vlan_offset + SingleVlanHeader::LEN { - assert_eq!( - Some(VlanHeader::Single(d.outer.clone())), - actual.vlan.as_ref().map(|v| v.to_header()) - ); - } else { - assert_eq!(None, actual.vlan); + let mut expected = ArrayVec::::new(); + for e in &test.link_exts { + match &e { + LinkExtHeader::Vlan(s) => { + if data.len() >= next_offset + s.header_len() { + expected.push(e.clone()); + next_offset += s.header_len(); + } else { + break; + } } - } - Some(VlanHeader::Single(s)) => { - if data.len() >= vlan_offset + SingleVlanHeader::LEN { - assert_eq!( - Some(VlanHeader::Single(s.clone())), - actual.vlan.as_ref().map(|v| v.to_header()) - ); - } else { - assert_eq!(None, actual.vlan); + LinkExtHeader::Macsec(m) => { + if data.len() >= next_offset + m.header_len() { + expected.push(e.clone()); + next_offset += m.header_len(); + } else { + break; + } } } - None => { - assert_eq!(None, actual.vlan); - } } + assert_eq!( + expected, + actual + .link_exts + .as_ref() + .iter() + .map(|v| v.to_header()) + .collect::>() + ); } fn compare_ip(test: &TestPacket, actual: &LaxSlicedPacket) { @@ -1343,13 +1771,13 @@ mod test { ); } - fn compare_ip_header_only(test: &TestPacket, actual: &LaxSlicedPacket) { + fn compare_net_header_only(test: &TestPacket, actual: &LaxSlicedPacket) { 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()), - NetHeaders::Arp(_) => unreachable!(), + NetHeaders::Arp(h) => NetHeaders::Arp(h.clone()), } }), actual.net.as_ref().map(|s| -> NetHeaders { @@ -1421,7 +1849,7 @@ mod test { test.link, actual.link.as_ref().map(|v| v.to_header()).flatten() ); - compare_vlan(test, data, &actual); + compare_link_exts(test, data, &actual); compare_ip(test, &actual); compare_transport( test, @@ -1430,21 +1858,24 @@ mod test { &actual, ); } - Some(Layer::VlanHeader) => { + Some(Layer::VlanHeader) | Some(Layer::MacsecHeader) => { assert_eq!( test.link, actual.link.as_ref().map(|v| v.to_header()).flatten() ); - compare_vlan(test, data, &actual); + compare_link_exts(test, data, &actual); assert_eq!(None, actual.net); assert_eq!(None, actual.transport); } - Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => { + Some(Layer::Ipv6Header) + | Some(Layer::Ipv4Header) + | Some(Layer::IpHeader) + | Some(Layer::Arp) => { assert_eq!( test.link, actual.link.as_ref().map(|v| v.to_header()).flatten() ); - compare_vlan(test, data, &actual); + compare_link_exts(test, data, &actual); assert_eq!(None, actual.net); assert_eq!(None, actual.transport); } @@ -1458,8 +1889,8 @@ mod test { test.link, actual.link.as_ref().map(|v| v.to_header()).flatten() ); - compare_vlan(test, data, &actual); - compare_ip_header_only(test, &actual); + compare_link_exts(test, data, &actual); + compare_net_header_only(test, &actual); assert_eq!(None, actual.transport); } Some(Layer::TcpHeader) @@ -1470,7 +1901,7 @@ mod test { test.link, actual.link.as_ref().map(|v| v.to_header()).flatten() ); - compare_vlan(test, data, &actual); + compare_link_exts(test, data, &actual); compare_ip(test, &actual); assert_eq!(None, actual.transport); } @@ -1479,18 +1910,23 @@ mod test { } } // from_ether_type (vlan at start) - if test.link.is_none() && test.vlan.is_some() { - for ether_type in VLAN_ETHER_TYPES { - let actual = LaxSlicedPacket::from_ether_type(ether_type, data); + if test.link.is_none() && !test.link_exts.is_empty() { + let ether_types: &[EtherType] = match test.link_exts.first().unwrap() { + LinkExtHeader::Vlan(_) => &VLAN_ETHER_TYPES, + LinkExtHeader::Macsec(_) => &MACSEC_ETHER_TYPES, + }; + for ether_type in ether_types { + let actual = LaxSlicedPacket::from_ether_type(*ether_type, data); assert_eq!(actual.stop_err, expected_stop_err); assert_eq!( Some(LinkSlice::EtherPayload(EtherPayloadSlice { - ether_type, + ether_type: *ether_type, + len_source: LenSource::Slice, payload: data })), actual.link ); - compare_vlan(test, data, &actual); + compare_link_exts(test, data, &actual); match expected_stop_err.as_ref().map(|v| v.1) { None => { compare_ip(test, &actual); @@ -1501,11 +1937,14 @@ mod test { &actual, ); } - Some(Layer::VlanHeader) => { + Some(Layer::VlanHeader) | Some(Layer::MacsecHeader) => { assert_eq!(None, actual.net); assert_eq!(None, actual.transport); } - Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => { + Some(Layer::Ipv6Header) + | Some(Layer::Ipv4Header) + | Some(Layer::IpHeader) + | Some(Layer::Arp) => { assert_eq!(None, actual.net); assert_eq!(None, actual.transport); } @@ -1515,7 +1954,7 @@ mod test { | Some(Layer::Ipv6DestOptionsHeader) | Some(Layer::Ipv6RouteHeader) | Some(Layer::Ipv6FragHeader) => { - compare_ip_header_only(test, &actual); + compare_net_header_only(test, &actual); assert_eq!(None, actual.transport); } Some(Layer::TcpHeader) @@ -1529,24 +1968,25 @@ mod test { } } } - // from_ether_type (ip at start) - if test.link.is_none() && test.vlan.is_none() { + // from_ether_type (net at start) + if test.link.is_none() && test.link_exts.is_empty() { if let Some(ip) = &test.net { let ether_type = match ip { NetHeaders::Ipv4(_, _) => ether_type::IPV4, NetHeaders::Ipv6(_, _) => ether_type::IPV6, - NetHeaders::Arp(_) => unreachable!(), + NetHeaders::Arp(_) => ether_type::ARP, }; let actual = LaxSlicedPacket::from_ether_type(ether_type, &data); assert_eq!(actual.stop_err, expected_stop_err); assert_eq!( Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type, + len_source: LenSource::Slice, payload: data })), actual.link ); - assert_eq!(test.vlan, None); + assert!(actual.link_exts.is_empty()); match expected_stop_err.as_ref().map(|v| v.1) { None => { compare_ip(test, &actual); @@ -1557,7 +1997,10 @@ mod test { &actual, ); } - Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => { + Some(Layer::Ipv6Header) + | Some(Layer::Ipv4Header) + | Some(Layer::IpHeader) + | Some(Layer::Arp) => { assert_eq!(None, actual.net); assert_eq!(None, actual.transport); } @@ -1567,7 +2010,7 @@ mod test { | Some(Layer::Ipv6DestOptionsHeader) | Some(Layer::Ipv6RouteHeader) | Some(Layer::Ipv6FragHeader) => { - compare_ip_header_only(test, &actual); + compare_net_header_only(test, &actual); assert_eq!(None, actual.transport); } Some(Layer::TcpHeader) @@ -1582,14 +2025,20 @@ mod test { } } // from_ip_slice - if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { + if test.link.is_none() + && test.link_exts.is_empty() + && matches!( + test.net, + Some(NetHeaders::Ipv4(_, _)) | Some(NetHeaders::Ipv6(_, _)) + ) + { if let Some(err) = expected_ip_err { assert_eq!(err, LaxSlicedPacket::from_ip(&data).unwrap_err()); } else { let actual = LaxSlicedPacket::from_ip(&data).unwrap(); assert_eq!(actual.stop_err, expected_stop_err); assert_eq!(actual.link, None); - assert_eq!(test.vlan, None); + assert!(actual.link_exts.is_empty()); match expected_stop_err.as_ref().map(|v| v.1) { None => { compare_ip(test, &actual); @@ -1606,7 +2055,7 @@ mod test { | Some(Layer::Ipv6DestOptionsHeader) | Some(Layer::Ipv6RouteHeader) | Some(Layer::Ipv6FragHeader) => { - compare_ip_header_only(test, &actual); + compare_net_header_only(test, &actual); assert_eq!(None, actual.transport); } Some(Layer::TcpHeader) diff --git a/etherparse/src/lax_sliced_packet_cursor.rs b/etherparse/src/lax_sliced_packet_cursor.rs index 9aff1ebd..b70fcb42 100644 --- a/etherparse/src/lax_sliced_packet_cursor.rs +++ b/etherparse/src/lax_sliced_packet_cursor.rs @@ -2,23 +2,25 @@ use crate::{ err::{packet::SliceError, Layer}, *, }; +use arrayvec::ArrayVec; /// Helper class for laxly slicing packets. pub(crate) struct LaxSlicedPacketCursor<'a> { pub offset: usize, + pub len_source: LenSource, pub result: LaxSlicedPacket<'a>, } impl<'a> LaxSlicedPacketCursor<'a> { pub fn parse_from_ethernet2(slice: &'a [u8]) -> Result, err::LenError> { - use ether_type::*; use LinkSlice::*; let mut cursor = LaxSlicedPacketCursor { offset: 0, + len_source: LenSource::Slice, result: LaxSlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, @@ -34,40 +36,31 @@ impl<'a> LaxSlicedPacketCursor<'a> { cursor.offset += result.header_len(); cursor.result.link = Some(Ethernet2(result)); - // continue parsing (if required) - match payload.ether_type { - IPV4 => Ok(cursor.slice_ip(payload.payload)), - IPV6 => Ok(cursor.slice_ip(payload.payload)), - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { - Ok(cursor.slice_vlan(payload.payload)) - } - _ => Ok(cursor.result), - } + // parse the rest + Ok(cursor.slice_ether_type(payload)) } pub fn parse_from_ether_type(ether_type: EtherType, slice: &'a [u8]) -> LaxSlicedPacket<'a> { let cursor = LaxSlicedPacketCursor { offset: 0, + len_source: LenSource::Slice, result: LaxSlicedPacket { link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type, + len_source: LenSource::Slice, payload: slice, })), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, stop_err: None, }, }; - use ether_type::*; - match ether_type { - IPV4 | IPV6 => cursor.slice_ip(slice), - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { - cursor.slice_vlan(slice) - } - ARP => cursor.slice_arp(slice), - _ => cursor.result, - } + cursor.slice_ether_type(EtherPayloadSlice { + ether_type, + len_source: LenSource::Slice, + payload: slice, + }) } pub fn parse_from_ip( @@ -82,9 +75,10 @@ impl<'a> LaxSlicedPacketCursor<'a> { let offset = (payload.payload.as_ptr() as usize) - (slice.as_ptr() as usize); Ok(LaxSlicedPacketCursor { offset, + len_source: LenSource::Slice, result: LaxSlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: Some(ip.into()), transport: None, stop_err: stop_err.map(|(stop_err, stop_layer)| { @@ -113,58 +107,96 @@ impl<'a> LaxSlicedPacketCursor<'a> { .slice_transport(payload)) } - pub fn slice_vlan(mut self, slice: &'a [u8]) -> LaxSlicedPacket<'a> { + pub fn slice_ether_type( + mut self, + mut ether_payload: EtherPayloadSlice<'a>, + ) -> LaxSlicedPacket<'a> { use ether_type::*; - use VlanSlice::*; - - // cache the starting slice so the later combining - // of outer & inner vlan is defined behavior (for miri) - let outer_start_slice = slice; - let outer = match SingleVlanSlice::from_slice(slice) { - Ok(v) => v, - Err(err) => { - self.result.stop_err = Some(( - SliceError::Len(err.add_offset(self.offset)), - Layer::VlanHeader, - )); - return self.result; - } - }; - self.result.vlan = Some(VlanSlice::SingleVlan(outer.clone())); - self.offset += outer.header_len(); - - //check if it is a double vlan header - match outer.ether_type() { - //in case of a double vlan header continue with the inner - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { - let inner = match SingleVlanSlice::from_slice(outer.payload_slice()) { - Ok(v) => v, - Err(err) => { - self.result.stop_err = Some(( - SliceError::Len(err.add_offset(self.offset)), - Layer::VlanHeader, - )); + loop { + match ether_payload.ether_type { + VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { + if self.result.link_exts.is_full() { + return self.result; + } + let vlan = match SingleVlanSlice::from_slice(ether_payload.payload) { + Ok(v) => v, + Err(err) => { + self.result.stop_err = Some(( + SliceError::Len(err.add_offset(self.offset)), + Layer::VlanHeader, + )); + return self.result; + } + }; + self.offset += vlan.header_len(); + let vlan_payload = vlan.payload(); + ether_payload = EtherPayloadSlice { + ether_type: vlan_payload.ether_type, + len_source: self.len_source, + payload: vlan_payload.payload, + }; + // SAFETY: Safe, as the if at the startt verifies that there is still + // space in link_exts. + unsafe { + self.result + .link_exts + .push_unchecked(LaxLinkExtSlice::Vlan(vlan)); + } + } + MACSEC => { + use err::macsec::HeaderSliceError as I; + if self.result.link_exts.is_full() { return self.result; } - }; - self.offset += inner.header_len(); + let macsec = match LaxMacsecSlice::from_slice(ether_payload.payload) { + Ok(v) => v, + Err(I::Len(err)) => { + let layer = err.layer; + self.result.stop_err = + Some((SliceError::Len(err.add_offset(self.offset)), layer)); + return self.result; + } + Err(I::Content(err)) => { + self.result.stop_err = + Some((SliceError::Macsec(err), Layer::MacsecHeader)); + return self.result; + } + }; - let inner_ether_type = inner.ether_type(); - self.result.vlan = Some(DoubleVlan(DoubleVlanSlice { - slice: outer_start_slice, - })); + self.offset += macsec.header.header_len(); + let macsec_payload = macsec.payload.clone(); + // SAFETY: Safe, as the if at the startt verifies that there is still + // space in link_exts. + unsafe { + self.result + .link_exts + .push_unchecked(LaxLinkExtSlice::Macsec(macsec)); + } - match inner_ether_type { - IPV4 => self.slice_ip(inner.payload_slice()), - IPV6 => self.slice_ip(inner.payload_slice()), - _ => self.result, + if let LaxMacsecPayloadSlice::Unmodified(e) = macsec_payload { + if e.len_source != LenSource::Slice { + self.len_source = e.len_source; + } + ether_payload = EtherPayloadSlice { + payload: e.payload, + len_source: self.len_source, + ether_type: e.ether_type, + }; + } else { + return self.result; + } + } + ARP => { + return self.slice_arp(ether_payload.payload); + } + IPV4 => return self.slice_ip(ether_payload.payload), + IPV6 => { + return self.slice_ip(ether_payload.payload); + } + _ => { + return self.result; } } - value => match value { - IPV4 => self.slice_ip(outer.payload_slice()), - IPV6 => self.slice_ip(outer.payload_slice()), - _ => self.result, - }, } } @@ -173,6 +205,9 @@ impl<'a> LaxSlicedPacketCursor<'a> { Ok(arp) => arp, Err(mut e) => { e.layer_start_offset += self.offset; + if LenSource::Slice == e.len_source { + e.len_source = self.len_source; + } self.result.stop_err = Some((err::packet::SliceError::Len(e), Layer::Arp)); return self.result; } @@ -191,6 +226,9 @@ impl<'a> LaxSlicedPacketCursor<'a> { self.result.stop_err = Some(match e { I::Len(mut l) => { l.layer_start_offset += self.offset; + if LenSource::Slice == l.len_source { + l.len_source = self.len_source; + } (O::Len(l), Layer::IpHeader) } I::Content(c) => (O::Ip(c), Layer::IpHeader), @@ -207,7 +245,13 @@ impl<'a> LaxSlicedPacketCursor<'a> { use err::packet::SliceError as O; self.result.stop_err = Some(( match stop_err { - I::Len(l) => O::Len(l.add_offset(self.offset)), + I::Len(mut l) => O::Len({ + l.layer_start_offset += self.offset; + if LenSource::Slice == l.len_source { + l.len_source = self.len_source; + } + l + }), I::Content(c) => match c { E::HopByHopNotAtStart => O::Ipv6Exts(E::HopByHopNotAtStart), E::IpAuth(auth) => match &ip.0 { @@ -223,13 +267,16 @@ impl<'a> LaxSlicedPacketCursor<'a> { // move offset for the transport layers let payload = ip.0.payload().clone(); self.offset += (payload.payload.as_ptr() as usize) - (slice.as_ptr() as usize); + if LenSource::Slice != payload.len_source { + self.len_source = payload.len_source; + } self.slice_transport(payload) } fn slice_transport(mut self, slice: LaxIpPayloadSlice<'a>) -> LaxSlicedPacket<'a> { use err::packet::SliceError as O; if slice.fragmented || self.result.stop_err.is_some() { - // if an error occured in an upper layer or the payload is fragmented + // if an error occurred in an upper layer or the payload is fragmented // stop here return self.result; } @@ -241,7 +288,9 @@ impl<'a> LaxSlicedPacketCursor<'a> { } Err(mut err) => { err.layer_start_offset += self.offset; - err.len_source = slice.len_source; + if LenSource::Slice == err.len_source { + err.len_source = slice.len_source; + } self.result.stop_err = Some((O::Len(err), Layer::Icmpv4)); } }, @@ -252,7 +301,9 @@ impl<'a> LaxSlicedPacketCursor<'a> { } Err(mut err) => { err.layer_start_offset += self.offset; - err.len_source = slice.len_source; + if LenSource::Slice == err.len_source { + err.len_source = slice.len_source; + } self.result.stop_err = Some((O::Len(err), Layer::UdpHeader)); } }, @@ -267,7 +318,9 @@ impl<'a> LaxSlicedPacketCursor<'a> { match err { I::Len(mut l) => { l.layer_start_offset += self.offset; - l.len_source = slice.len_source; + if LenSource::Slice == l.len_source { + l.len_source = slice.len_source; + } O::Len(l) } I::Content(c) => O::Tcp(c), @@ -283,7 +336,9 @@ impl<'a> LaxSlicedPacketCursor<'a> { } Err(mut err) => { err.layer_start_offset += self.offset; - err.len_source = slice.len_source; + if LenSource::Slice == err.len_source { + err.len_source = slice.len_source; + } self.result.stop_err = Some((O::Len(err), Layer::Icmpv6)); } }, diff --git a/etherparse/src/len_source.rs b/etherparse/src/len_source.rs index 16ff5c7d..39beff9b 100644 --- a/etherparse/src/len_source.rs +++ b/etherparse/src/len_source.rs @@ -4,6 +4,8 @@ pub enum LenSource { /// Limiting length was the slice length (we don't know what determined /// that one originally). Slice, + /// Short length field in the MACsec header. + MacsecShortLength, /// Length Ipv4HeaderTotalLen, /// Error occurred in the IPv6 layer. diff --git a/etherparse/src/lib.rs b/etherparse/src/lib.rs index 59fad735..31630cd1 100644 --- a/etherparse/src/lib.rs +++ b/etherparse/src/lib.rs @@ -3,6 +3,7 @@ //! Currently supported are: //! * Ethernet II //! * IEEE 802.1Q VLAN Tagging Header +//! * MACsec (IEEE 802.1AE) //! * ARP //! * IPv4 //! * IPv6 (supporting the most common extension headers, but not all) @@ -57,11 +58,11 @@ //! Err(value) => println!("Err {:?}", value), //! Ok(value) => { //! println!("link: {:?}", value.link); -//! println!("vlan: {:?}", value.vlan); +//! println!("link_exts: {:?}", value.link_exts); // contains vlan & macsec //! println!("net: {:?}", value.net); // contains ip & arp //! println!("transport: {:?}", value.transport); //! } -//! } +//! }; //! ``` //! This is the faster option if your code is not interested in all fields of all the headers. It is a good choice if you just want filter or find packages based on a subset of the headers and/or their fields. //! @@ -101,11 +102,11 @@ //! Err(value) => println!("Err {:?}", value), //! Ok(value) => { //! println!("link: {:?}", value.link); -//! println!("vlan: {:?}", value.vlan); +//! println!("link_exts: {:?}", value.link_exts); // contains vlan & macsec //! println!("net: {:?}", value.net); // contains ip & arp //! println!("transport: {:?}", value.transport); //! } -//! } +//! }; //! ``` //! This option is slower then slicing when only few fields are accessed. But it can be the faster option or useful if you are interested in most fields anyways or if you want to re-serialize the headers with modified values. //! @@ -127,7 +128,8 @@ //! //! * [`Ethernet2Slice::from_slice_without_fcs`] & [`Ethernet2Slice::from_slice_with_crc32_fcs`] //! * [`LinuxSllSlice::from_slice`] -//! * [`SingleVlanSlice::from_slice`] & [`DoubleVlanSlice::from_slice`] +//! * [`SingleVlanSlice::from_slice`] +//! * [`MacsecSlice::from_slice`] //! * [`IpSlice::from_slice`] & [`LaxIpSlice::from_slice`] //! * [`Ipv4Slice::from_slice`] & [`LaxIpv4Slice::from_slice`] //! * [`Ipv6Slice::from_slice`] & [`LaxIpv6Slice::from_slice`] @@ -149,7 +151,7 @@ //! * [`Ethernet2HeaderSlice::from_slice`] //! * [`LinuxSllHeaderSlice::from_slice`] //! * [`SingleVlanHeaderSlice::from_slice`] -//! * [`DoubleVlanHeaderSlice::from_slice`] +//! * [`MacsecHeaderSlice::from_slice`] //! * [`ArpPacketSlice::from_slice`] //! * [`Ipv4HeaderSlice::from_slice`] //! * [`Ipv4ExtensionsSlice::from_slice`] @@ -166,7 +168,7 @@ //! * [`Ethernet2Header::read`] & [`Ethernet2Header::from_slice`] //! * [`LinuxSllHeader::read`] & [`LinuxSllHeader::from_slice`] //! * [`SingleVlanHeader::read`] & [`SingleVlanHeader::from_slice`] -//! * [`DoubleVlanHeader::read`] & [`DoubleVlanHeader::from_slice`] +//! * [`MacsecHeader::read`] & [`MacsecHeader::from_slice`] //! * [`ArpPacket::read`] & [`ArpPacket::from_slice`] //! * [`IpHeaders::read`] & [`IpHeaders::from_slice`] //! * [`Ipv4Header::read`] & [`Ipv4Header::from_slice`] @@ -231,7 +233,7 @@ //! * [`Ethernet2Header::to_bytes`] & [`Ethernet2Header::write`] //! * [`LinuxSllHeader::to_bytes`] & [`LinuxSllHeader::write`] //! * [`SingleVlanHeader::to_bytes`] & [`SingleVlanHeader::write`] -//! * [`DoubleVlanHeader::to_bytes`] & [`DoubleVlanHeader::write`] +//! * [`MacsecHeader::to_bytes`] & [`MacsecHeader::write`] //! * [`ArpPacket::to_bytes`] & [`ArpPacket::write`] //! * [`ArpEthIpv4Packet::to_bytes`] //! * [`Ipv4Header::to_bytes`] & [`Ipv4Header::write`] & [`Ipv4Header::write_raw`] @@ -277,6 +279,8 @@ //! * [Linux packet types definitions](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_packet.h?id=e33c4963bf536900f917fb65a687724d5539bc21) on the Linux kernel //! * Address Resolution Protocol (ARP) Parameters [Harware Types](https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2) //! * [Arp hardware identifiers definitions](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_arp.h?id=e33c4963bf536900f917fb65a687724d5539bc21) on the Linux kernel +//! * ["IEEE Standard for Local and metropolitan area networks-Media Access Control (MAC) Security," in IEEE Std 802.1AE-2018 (Revision of IEEE Std 802.1AE-2006) , vol., no., pp.1-239, 26 Dec. 2018, doi: 10.1109/IEEESTD.2018.8585421.](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8585421&isnumber=8585420) +//! * ["IEEE Standard for Local and metropolitan area networks--Media Access Control (MAC) Security Corrigendum 1: Tag Control Information Figure," in IEEE Std 802.1AE-2018/Cor 1-2020 (Corrigendum to IEEE Std 802.1AE-2018) , vol., no., pp.1-14, 21 July 2020, doi: 10.1109/IEEESTD.2020.9144679.](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=9144679&isnumber=9144678) // # Reason for 'bool_comparison' disable: // diff --git a/etherparse/src/link/double_vlan_header.rs b/etherparse/src/link/double_vlan_header.rs index dd2aed11..4e033e23 100644 --- a/etherparse/src/link/double_vlan_header.rs +++ b/etherparse/src/link/double_vlan_header.rs @@ -1,6 +1,12 @@ use crate::*; -/// IEEE 802.1Q double VLAN Tagging Header +/// IEEE 802.1Q double VLAN Tagging Header (helper struct to +/// check vlan tagging values in a [crate::`PacketHeaders`]). +/// +/// Note that it is NOT guranteed that the two VLAN headers +/// will directly follow each other. In the original packet +/// there could be another LinkExt header present in between +/// them (e.g. MacSec Security Tag). #[derive(Clone, Debug, Eq, PartialEq)] pub struct DoubleVlanHeader { /// The outer vlan tagging header @@ -9,84 +15,6 @@ pub struct DoubleVlanHeader { pub inner: SingleVlanHeader, } -impl DoubleVlanHeader { - /// Serialized size of two VLAN headers in bytes/octets. - pub const LEN: usize = 8; - - #[deprecated(since = "0.14.0", note = "Use `DoubleVlanHeader::LEN` instead")] - pub const SERIALIZED_SIZE: usize = DoubleVlanHeader::LEN; - - /// Read an DoubleVlanHeader from a slice and return the header & unused parts of the slice. - #[deprecated(since = "0.10.1", note = "Use SingleVlanHeader::from_slice instead.")] - #[inline] - pub fn read_from_slice( - slice: &[u8], - ) -> Result<(DoubleVlanHeader, &[u8]), err::double_vlan::HeaderSliceError> { - DoubleVlanHeader::from_slice(slice) - } - - /// Read an DoubleVlanHeader from a slice and return the header & unused parts of the slice. - #[inline] - pub fn from_slice( - slice: &[u8], - ) -> Result<(DoubleVlanHeader, &[u8]), err::double_vlan::HeaderSliceError> { - Ok(( - DoubleVlanHeaderSlice::from_slice(slice)?.to_header(), - &slice[DoubleVlanHeader::LEN..], - )) - } - - /// Read a double tagging header from the given source - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - pub fn read( - reader: &mut T, - ) -> Result { - use err::double_vlan::{HeaderError::*, HeaderReadError::*}; - - let outer = SingleVlanHeader::read(reader).map_err(Io)?; - - use crate::ether_type::{PROVIDER_BRIDGING, VLAN_DOUBLE_TAGGED_FRAME, VLAN_TAGGED_FRAME}; - //check that outer ethertype is matching - match outer.ether_type { - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { - Ok(DoubleVlanHeader { - outer, - inner: SingleVlanHeader::read(reader).map_err(Io)?, - }) - } - value => Err(Content(NonVlanEtherType { - unexpected_ether_type: value, - })), - } - } - - /// Write the double IEEE 802.1Q VLAN tagging header - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { - self.outer.write(writer)?; - self.inner.write(writer) - } - - /// Length of the serialized headers in bytes. - #[inline] - pub fn header_len(&self) -> usize { - 8 - } - - /// Returns the serialized form of the headers or an value error in case - /// the headers contain values that are outside of range. - #[inline] - pub fn to_bytes(&self) -> [u8; 8] { - let outer = self.outer.to_bytes(); - let inner = self.inner.to_bytes(); - [ - outer[0], outer[1], outer[2], outer[3], inner[0], inner[1], inner[2], inner[3], - ] - } -} - impl Default for DoubleVlanHeader { fn default() -> Self { DoubleVlanHeader { @@ -104,175 +32,8 @@ impl Default for DoubleVlanHeader { #[cfg(test)] mod test { use crate::{test_gens::*, *}; - use alloc::{format, vec::Vec}; + use alloc::format; use proptest::prelude::*; - use std::io::{Cursor, ErrorKind}; - - #[test] - fn constants() { - assert_eq!(8, DoubleVlanHeader::LEN); - } - - proptest! { - #[test] - fn from_slice( - input in vlan_double_any(), - dummy_data in proptest::collection::vec(any::(), 0..20), - ether_type_non_vlan in ether_type_any().prop_filter( - "ether_type must not be a vlan ether type", - |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) - ) - ) { - use err::double_vlan::{HeaderError::*, HeaderSliceError::*}; - - // serialize - let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); - input.write(&mut buffer).unwrap(); - buffer.extend(&dummy_data[..]); - - // normal - { - let (result, rest) = DoubleVlanHeader::from_slice(&buffer).unwrap(); - assert_eq!(result, input); - assert_eq!(rest, &buffer[8..]); - } - #[allow(deprecated)] - { - let (result, rest) = DoubleVlanHeader::read_from_slice(&buffer).unwrap(); - assert_eq!(result, input); - assert_eq!(rest, &buffer[8..]); - } - - // slice length to small - for len in 0..8 { - assert_eq!( - DoubleVlanHeader::from_slice(&buffer[..len]) - .unwrap_err(), - Len(err::LenError{ - required_len: 8, - len: len, - len_source: LenSource::Slice, - layer: err::Layer::VlanHeader, - layer_start_offset: 0, - }) - ); - } - - // bad outer ether type - { - let mut bad_outer = input.clone(); - bad_outer.outer.ether_type = ether_type_non_vlan; - let bytes = bad_outer.to_bytes(); - assert_eq!( - DoubleVlanHeader::from_slice(&bytes) - .unwrap_err(), - Content(NonVlanEtherType{ - unexpected_ether_type: ether_type_non_vlan, - }) - ); - } - } - } - - proptest! { - #[test] - fn read( - input in vlan_double_any(), - dummy_data in proptest::collection::vec(any::(), 0..20), - ether_type_non_vlan in ether_type_any().prop_filter( - "ether_type must not be a vlan ether type", - |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) - ) - ) { - use err::double_vlan::HeaderError::*; - - // serialize - let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); - input.write(&mut buffer).unwrap(); - buffer.extend(&dummy_data[..]); - - // normal - { - let mut cursor = Cursor::new(&buffer); - let result = DoubleVlanHeader::read(&mut cursor).unwrap(); - assert_eq!(result, input); - assert_eq!(8, cursor.position()); - } - - // outer & inner error - for len in 0..8 { - let mut cursor = Cursor::new(&buffer[0..len]); - assert_eq!( - DoubleVlanHeader::read(&mut cursor) - .unwrap_err() - .io_error() - .unwrap() - .kind(), - ErrorKind::UnexpectedEof - ); - } - - // bad outer ether type - { - let mut bad_outer = input.clone(); - bad_outer.outer.ether_type = ether_type_non_vlan; - let bytes = bad_outer.to_bytes(); - let mut cursor = Cursor::new(&bytes); - assert_eq!( - DoubleVlanHeader::read(&mut cursor) - .unwrap_err() - .content_error() - .unwrap(), - NonVlanEtherType{ - unexpected_ether_type: ether_type_non_vlan, - } - ); - } - } - } - - proptest! { - #[test] - fn write_and_to_bytes(input in vlan_double_any()) { - // normal write - { - let mut buffer: Vec = Vec::with_capacity(input.header_len()); - input.write(&mut buffer).unwrap(); - assert_eq!(&buffer[..], &input.to_bytes()); - { - let inner_bytes = input.inner.to_bytes(); - let outer_bytes = input.outer.to_bytes(); - assert_eq!( - input.to_bytes(), - [ - outer_bytes[0], - outer_bytes[1], - outer_bytes[2], - outer_bytes[3], - inner_bytes[0], - inner_bytes[1], - inner_bytes[2], - inner_bytes[3], - ] - ); - } - } - - // io error - for len in 0..DoubleVlanHeader::LEN { - let mut buf = [0u8;DoubleVlanHeader::LEN]; - let mut cursor = Cursor::new(&mut buf[..len]); - assert!(input.write(&mut cursor).is_err()); - } - } - } - - proptest! { - #[test] - fn header_len(input in vlan_double_any()) { - assert_eq!(8, input.header_len()); - } - } #[test] fn default() { diff --git a/etherparse/src/link/double_vlan_header_slice.rs b/etherparse/src/link/double_vlan_header_slice.rs deleted file mode 100644 index 38a4945f..00000000 --- a/etherparse/src/link/double_vlan_header_slice.rs +++ /dev/null @@ -1,204 +0,0 @@ -use crate::*; -use core::slice::from_raw_parts; - -/// A slice containing an double vlan header of a network package. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DoubleVlanHeaderSlice<'a> { - pub(crate) slice: &'a [u8], -} - -impl<'a> DoubleVlanHeaderSlice<'a> { - /// Creates a double header slice from a slice. - pub fn from_slice( - slice: &'a [u8], - ) -> Result, err::double_vlan::HeaderSliceError> { - use err::double_vlan::{HeaderError::*, HeaderSliceError::*}; - - // check length - if slice.len() < DoubleVlanHeader::LEN { - return Err(Len(err::LenError { - required_len: DoubleVlanHeader::LEN, - len: slice.len(), - len_source: LenSource::Slice, - layer: err::Layer::VlanHeader, - layer_start_offset: 0, - })); - } - - // create slice - let result = DoubleVlanHeaderSlice { - // SAFETY: - // Safe as the slice length is checked is before to have - // at least the length of DoubleVlanHeader::LEN (8) - slice: unsafe { from_raw_parts(slice.as_ptr(), DoubleVlanHeader::LEN) }, - }; - - use ether_type::*; - - //check that outer ethertype is matching - match result.outer().ether_type() { - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { - //all done - Ok(result) - } - value => Err(Content(NonVlanEtherType { - unexpected_ether_type: value, - })), - } - } - - /// Returns the slice containing the double vlan header - #[inline] - pub fn slice(&self) -> &'a [u8] { - self.slice - } - - /// Returns a slice with the outer vlan header - #[inline] - pub fn outer(&self) -> SingleVlanHeaderSlice<'a> { - // SAFETY: - // Safe as the constructor checks that the slice has the length - // of DoubleVlanHeader::LEN (8) and the - // SingleVlanHeader::LEN has a size of 4. - unsafe { - SingleVlanHeaderSlice::from_slice_unchecked(from_raw_parts( - self.slice.as_ptr(), - SingleVlanHeader::LEN, - )) - } - } - - /// Returns a slice with the inner vlan header. - #[inline] - pub fn inner(&self) -> SingleVlanHeaderSlice<'a> { - // SAFETY: - // Safe as the constructor checks that the slice has the length - // of DoubleVlanHeader::LEN (8) and the - // SingleVlanHeader::LEN has a size of 4. - unsafe { - SingleVlanHeaderSlice::from_slice_unchecked(from_raw_parts( - self.slice.as_ptr().add(SingleVlanHeader::LEN), - SingleVlanHeader::LEN, - )) - } - } - - /// Decode all the fields and copy the results to a DoubleVlanHeader struct - pub fn to_header(&self) -> DoubleVlanHeader { - DoubleVlanHeader { - outer: self.outer().to_header(), - inner: self.inner().to_header(), - } - } -} - -#[cfg(test)] -mod test { - use crate::{test_gens::*, *}; - use alloc::{format, vec::Vec}; - use proptest::prelude::*; - - proptest! { - #[test] - fn from_slice( - input in vlan_double_any(), - dummy_data in proptest::collection::vec(any::(), 0..20), - ether_type_non_vlan in ether_type_any().prop_filter( - "ether_type must not be a vlan ether type", - |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) - ) - ) { - use err::double_vlan::{HeaderError::*, HeaderSliceError::*}; - { - // serialize - let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); - input.write(&mut buffer).unwrap(); - buffer.extend(&dummy_data[..]); - - // normal - { - let slice = DoubleVlanHeaderSlice::from_slice(&buffer).unwrap(); - assert_eq!(slice.slice(), &buffer[..8]); - } - - // slice length to small - for len in 0..8 { - assert_eq!( - DoubleVlanHeaderSlice::from_slice(&buffer[..len]) - .unwrap_err(), - - Len(err::LenError{ - required_len: 8, - len: len, - len_source: LenSource::Slice, - layer: err::Layer::VlanHeader, - layer_start_offset: 0, - }) - ); - } - } - - // bad outer ether type - { - let mut bad_outer = input.clone(); - bad_outer.outer.ether_type = ether_type_non_vlan; - assert_eq!( - DoubleVlanHeaderSlice::from_slice(&bad_outer.to_bytes()) - .unwrap_err(), - Content(NonVlanEtherType{ unexpected_ether_type: ether_type_non_vlan }) - ); - } - } - } - - proptest! { - #[test] - fn getters(input in vlan_double_any()) { - let bytes = input.to_bytes(); - let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); - - assert_eq!(input.outer, slice.outer().to_header()); - assert_eq!(input.inner, slice.inner().to_header()); - } - } - - proptest! { - #[test] - fn to_header(input in vlan_double_any()) { - let bytes = input.to_bytes(); - let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); - - assert_eq!( - DoubleVlanHeader{ - outer: input.outer, - inner: input.inner, - }, - slice.to_header() - ); - } - } - - proptest! { - #[test] - fn clone_eq(input in vlan_double_any()) { - let bytes = input.to_bytes(); - let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); - assert_eq!(slice, slice.clone()); - } - } - - proptest! { - #[test] - fn dbg(input in vlan_double_any()) { - let bytes = input.to_bytes(); - let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); - assert_eq!( - &format!( - "DoubleVlanHeaderSlice {{ slice: {:?} }}", - slice.slice(), - ), - &format!("{:?}", slice) - ); - } - } -} diff --git a/etherparse/src/link/double_vlan_slice.rs b/etherparse/src/link/double_vlan_slice.rs index 7b2de5f9..48ae122a 100644 --- a/etherparse/src/link/double_vlan_slice.rs +++ b/etherparse/src/link/double_vlan_slice.rs @@ -1,76 +1,28 @@ -use crate::{err::*, *}; - -/// Slice containing a VLAN header & payload. +use crate::*; + +/// Two IEEE 802.1Q VLAN Tagging headers & payload slices (helper +/// struct to check vlan tagging values in a [crate::`SlicedPacket`]). +/// +/// Note that it is NOT guaranteed that the two VLAN headers directly +/// followed each other in the original packet. In the original packet +/// there could be another LinkExt header present in between them (e.g. +/// a MacSec Security Tag). #[derive(Clone, Eq, PartialEq)] pub struct DoubleVlanSlice<'a> { - pub(crate) slice: &'a [u8], + /// Outer VLAN header & payload (should include the header of inner vlan header + /// and potentially additional link extension headers (e.g. MacSec) in between). + pub outer: SingleVlanSlice<'a>, + /// Inner VLAN header & payload. + pub inner: SingleVlanSlice<'a>, } impl<'a> DoubleVlanSlice<'a> { - /// Try creating a [`DoubleVlanSlice`] from a slice containing the - /// VLAN header & payload. - pub fn from_slice( - slice: &'a [u8], - ) -> Result, err::double_vlan::HeaderSliceError> { - use err::double_vlan::{HeaderError::*, HeaderSliceError::*}; - - // check length - if slice.len() < DoubleVlanHeader::LEN { - return Err(Len(LenError { - required_len: DoubleVlanHeader::LEN, - len: slice.len(), - len_source: LenSource::Slice, - layer: Layer::VlanHeader, - layer_start_offset: 0, - })); - } - - // create slice - let result = DoubleVlanSlice { slice }; - - // check that outer ethertype is matching - use ether_type::*; - match result.outer().ether_type() { - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => Ok(result), - value => Err(Content(NonVlanEtherType { - unexpected_ether_type: value, - })), - } - } - - /// Returns the slice containing the VLAN header and payload. - #[inline] - pub fn slice(&self) -> &'a [u8] { - self.slice - } - - /// Outer VLAN header & payload (includes header of inner vlan header). - #[inline] - pub fn outer(&self) -> SingleVlanSlice { - SingleVlanSlice { slice: self.slice } - } - - /// Inner VLAN header & payload. - #[inline] - pub fn inner(&self) -> SingleVlanSlice { - SingleVlanSlice { - slice: unsafe { - // SAFETY: Safe as "from_slice" verified the slice length - // to be DoubleVlanHeader::LEN (aka 2*SingleVlanHeader::LEN). - core::slice::from_raw_parts( - self.slice.as_ptr().add(SingleVlanHeader::LEN), - self.slice.len() - SingleVlanHeader::LEN, - ) - }, - } - } - /// Decode all the fields and copy the results to a DoubleVlanHeader struct #[inline] pub fn to_header(&self) -> DoubleVlanHeader { DoubleVlanHeader { - outer: self.outer().to_header(), - inner: self.inner().to_header(), + outer: self.outer.to_header(), + inner: self.inner.to_header(), } } @@ -78,39 +30,21 @@ impl<'a> DoubleVlanSlice<'a> { /// identifying it's content type after bother VLAN headers. #[inline] pub fn payload(&self) -> EtherPayloadSlice<'a> { - EtherPayloadSlice { - ether_type: self.inner().ether_type(), - payload: self.payload_slice(), - } + self.inner.payload() } /// Returns the slice containing the payload after both /// VLAN headers. pub fn payload_slice(&self) -> &'a [u8] { - unsafe { - // SAFETY: - // Safe as the contructor checks that the slice has - // at least the length of DoubleVlanHeader::LEN (8). - core::slice::from_raw_parts( - self.slice.as_ptr().add(DoubleVlanHeader::LEN), - self.slice.len() - DoubleVlanHeader::LEN, - ) - } - } - - /// Length of the VLAN header in bytes (equal to - /// [`crate::DoubleVlanHeader::LEN`]). - #[inline] - pub const fn header_len(&self) -> usize { - DoubleVlanHeader::LEN + self.inner.payload_slice() } } impl core::fmt::Debug for DoubleVlanSlice<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("DoubleVlanSlice") - .field("outer", &self.outer().to_header()) - .field("inner", &self.inner().to_header()) + .field("outer", &self.outer.to_header()) + .field("inner", &self.inner.to_header()) .field("payload", &self.payload()) .finish() } @@ -130,22 +64,27 @@ mod test { ) { let payload: [u8;8] = [1,2,3,4,5,6,7,8]; let mut data = Vec::with_capacity( - vlan.header_len() + + vlan.outer.header_len() + + vlan.inner.header_len() + payload.len() ); - data.extend_from_slice(&vlan.to_bytes()); + data.extend_from_slice(&vlan.outer.to_bytes()); + data.extend_from_slice(&vlan.inner.to_bytes()); data.extend_from_slice(&payload); // decode packet - let slice = DoubleVlanSlice::from_slice(&data).unwrap(); + let slice = DoubleVlanSlice{ + outer: SingleVlanSlice::from_slice(&data).unwrap(), + inner: SingleVlanSlice::from_slice(&data[SingleVlanHeader::LEN..]).unwrap(), + }; // check debug output prop_assert_eq!( format!("{:?}", slice), format!( "DoubleVlanSlice {{ outer: {:?}, inner: {:?}, payload: {:?} }}", - slice.outer().to_header(), - slice.inner().to_header(), + slice.outer.to_header(), + slice.inner.to_header(), slice.payload(), ) ); @@ -158,87 +97,32 @@ mod test { fn getters(vlan in vlan_double_any()) { let payload: [u8;8] = [1,2,3,4,5,6,7,8]; let mut data = Vec::with_capacity( - vlan.header_len() + + vlan.outer.header_len() + + vlan.inner.header_len() + payload.len() ); - data.extend_from_slice(&vlan.to_bytes()); + data.extend_from_slice(&vlan.outer.to_bytes()); + data.extend_from_slice(&vlan.inner.to_bytes()); data.extend_from_slice(&payload); - let slice = DoubleVlanSlice::from_slice(&data).unwrap(); - assert_eq!(&data, slice.slice()); - assert_eq!(&data, slice.outer().slice()); - assert_eq!(vlan.outer, slice.outer().to_header()); - assert_eq!(&data[SingleVlanHeader::LEN..], slice.inner().slice()); - assert_eq!(vlan.inner, slice.inner().to_header()); + let slice = DoubleVlanSlice{ + outer: SingleVlanSlice::from_slice(&data).unwrap(), + inner: SingleVlanSlice::from_slice(&data[SingleVlanHeader::LEN..]).unwrap(), + }; + assert_eq!(&data, slice.outer.slice()); + assert_eq!(vlan.outer, slice.outer.to_header()); + assert_eq!(&data[SingleVlanHeader::LEN..], slice.inner.slice()); + assert_eq!(vlan.inner, slice.inner.to_header()); assert_eq!(vlan, slice.to_header()); assert_eq!( EtherPayloadSlice{ ether_type: vlan.inner.ether_type, - payload: &payload + len_source: LenSource::Slice, + payload: &payload, }, slice.payload() ); assert_eq!(&payload, slice.payload_slice()); - assert_eq!(DoubleVlanHeader::LEN, slice.header_len()); - } - } - - proptest! { - #[test] - fn from_slice( - vlan in vlan_double_any(), - ether_type_non_vlan in ether_type_any().prop_filter( - "ether_type must not be a vlan ether type", - |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) - ) - ) { - use err::double_vlan::{HeaderError::*, HeaderSliceError::*}; - - let payload: [u8;10] = [1,2,3,4,5,6,7,8,9,10]; - let data = { - let mut data = Vec::with_capacity( - vlan.header_len() + - payload.len() - ); - data.extend_from_slice(&vlan.to_bytes()); - data.extend_from_slice(&payload); - data - }; - - // normal decode - { - let slice = DoubleVlanSlice::from_slice(&data).unwrap(); - assert_eq!(slice.to_header(), vlan); - assert_eq!(slice.payload_slice(), &payload); - } - - // length error - for len in 0..DoubleVlanHeader::LEN { - assert_eq!( - DoubleVlanSlice::from_slice(&data[..len]).unwrap_err(), - Len(LenError{ - required_len: DoubleVlanHeader::LEN, - len, - len_source: LenSource::Slice, - layer: Layer::VlanHeader, - layer_start_offset: 0 - }) - ); - } - - // mismatching outer ether type - { - let mut bad_data = data.clone(); - let e_be = ether_type_non_vlan.0.to_be_bytes(); - bad_data[2] = e_be[0]; - bad_data[3] = e_be[1]; - assert_eq!( - DoubleVlanSlice::from_slice(&bad_data).unwrap_err(), - Content(NonVlanEtherType{ - unexpected_ether_type: ether_type_non_vlan, - }) - ); - } } } } diff --git a/etherparse/src/link/ether_payload_slice.rs b/etherparse/src/link/ether_payload_slice.rs index 0a19e81a..eb79dd20 100644 --- a/etherparse/src/link/ether_payload_slice.rs +++ b/etherparse/src/link/ether_payload_slice.rs @@ -1,11 +1,15 @@ use crate::*; -/// Payload of an IP packet. +/// Payload of an link layer packet. #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] pub struct EtherPayloadSlice<'a> { /// Identifying content of the payload. pub ether_type: EtherType, + /// Length field that was used to determine the length + /// of the payload (e.g. MACsec "short length" field). + pub len_source: LenSource, + /// Payload pub payload: &'a [u8], } @@ -20,11 +24,12 @@ mod test { let s = EtherPayloadSlice { ether_type: EtherType::IPV4, payload: &[], + len_source: LenSource::MacsecShortLength, }; assert_eq!( format!( - "EtherPayloadSlice {{ ether_type: {:?}, payload: {:?} }}", - s.ether_type, s.payload + "EtherPayloadSlice {{ ether_type: {:?}, len_source: {:?}, payload: {:?} }}", + s.ether_type, s.len_source, s.payload ), format!("{:?}", s) ); @@ -35,6 +40,7 @@ mod test { let s = EtherPayloadSlice { ether_type: EtherType::IPV4, payload: &[], + len_source: LenSource::MacsecShortLength, }; assert_eq!(s.clone(), s); diff --git a/etherparse/src/link/ether_type_impl.rs b/etherparse/src/link/ether_type_impl.rs index 9cf65188..635bf927 100644 --- a/etherparse/src/link/ether_type_impl.rs +++ b/etherparse/src/link/ether_type_impl.rs @@ -1,4 +1,4 @@ -/// Represents an "Ethertype" present in a Ethernet II header. +/// Represents an "ether type" present in a Ethernet II header. /// /// You can access the underlying `u16` value by using `.0` and any `u16` /// can be converted to an `EtherType`: @@ -37,6 +37,9 @@ impl EtherType { pub const WAKE_ON_LAN: EtherType = Self(0x0842); pub const VLAN_TAGGED_FRAME: EtherType = Self(0x8100); pub const PROVIDER_BRIDGING: EtherType = Self(0x88A8); + + /// IEEE Std 802.1AE - Media Access Control Security + pub const MACSEC: EtherType = Self(0x88E5); pub const VLAN_DOUBLE_TAGGED_FRAME: EtherType = Self(0x9100); } @@ -71,6 +74,11 @@ impl core::fmt::Debug for EtherType { "{:#06X} (IEEE Std 802.1Q - Service VLAN tag identifier (S-Tag))", self.0 ), + Self::MACSEC => write!( + f, + "{:#06X} (IEEE Std 802.1AE - Media Access Control Security)", + self.0 + ), Self::VLAN_DOUBLE_TAGGED_FRAME => { write!(f, "{:#06X} (VLAN Double Tagged Frame)", self.0) } @@ -105,6 +113,9 @@ pub mod ether_type { pub const WAKE_ON_LAN: EtherType = EtherType::WAKE_ON_LAN; pub const VLAN_TAGGED_FRAME: EtherType = EtherType::VLAN_TAGGED_FRAME; pub const PROVIDER_BRIDGING: EtherType = EtherType::PROVIDER_BRIDGING; + + /// IEEE Std 802.1AE - Media Access Control Security + pub const MACSEC: EtherType = EtherType::MACSEC; pub const VLAN_DOUBLE_TAGGED_FRAME: EtherType = EtherType::VLAN_DOUBLE_TAGGED_FRAME; } @@ -145,6 +156,7 @@ mod test { (EtherType::ARP, ARP), (EtherType::WAKE_ON_LAN, WAKE_ON_LAN), (EtherType::VLAN_TAGGED_FRAME, VLAN_TAGGED_FRAME), + (EtherType::MACSEC, MACSEC), (EtherType::PROVIDER_BRIDGING, PROVIDER_BRIDGING), ( EtherType::VLAN_DOUBLE_TAGGED_FRAME, @@ -178,6 +190,10 @@ mod test { EtherType::PROVIDER_BRIDGING, "0x88A8 (IEEE Std 802.1Q - Service VLAN tag identifier (S-Tag))", ), + ( + EtherType::MACSEC, + "0x88E5 (IEEE Std 802.1AE - Media Access Control Security)", + ), ( EtherType::VLAN_DOUBLE_TAGGED_FRAME, "0x9100 (VLAN Double Tagged Frame)", diff --git a/etherparse/src/link/ethernet2_slice.rs b/etherparse/src/link/ethernet2_slice.rs index b3097079..97e95102 100644 --- a/etherparse/src/link/ethernet2_slice.rs +++ b/etherparse/src/link/ethernet2_slice.rs @@ -60,7 +60,7 @@ impl<'a> Ethernet2Slice<'a> { #[inline] pub fn destination(&self) -> [u8; 6] { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of Ethernet2Header::LEN (14). unsafe { get_unchecked_6_byte_array(self.slice.as_ptr()) } } @@ -69,7 +69,7 @@ impl<'a> Ethernet2Slice<'a> { #[inline] pub fn source(&self) -> [u8; 6] { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of Ethernet2Header::LEN (14). unsafe { get_unchecked_6_byte_array(self.slice.as_ptr().add(6)) } } @@ -79,7 +79,7 @@ impl<'a> Ethernet2Slice<'a> { #[inline] pub fn ether_type(&self) -> EtherType { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of Ethernet2Header::LEN (14). EtherType(unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(12)) }) } @@ -117,7 +117,7 @@ impl<'a> Ethernet2Slice<'a> { pub fn header_slice(&self) -> &[u8] { unsafe { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of Ethernet2Header::LEN (14). core::slice::from_raw_parts(self.slice.as_ptr(), Ethernet2Header::LEN) } @@ -129,6 +129,7 @@ impl<'a> Ethernet2Slice<'a> { pub fn payload(&self) -> EtherPayloadSlice<'a> { EtherPayloadSlice { ether_type: self.ether_type(), + len_source: LenSource::Slice, payload: self.payload_slice(), } } @@ -228,6 +229,7 @@ mod test { assert_eq!( EtherPayloadSlice{ payload: &payload, + len_source: LenSource::Slice, ether_type: eth.ether_type, }, slice.payload() @@ -246,8 +248,9 @@ mod test { assert_eq!(&payload[..payload.len() - 4], slice.payload_slice()); assert_eq!( EtherPayloadSlice{ - payload: &payload[..payload.len() - 4], ether_type: eth.ether_type, + len_source: LenSource::Slice, + payload: &payload[..payload.len() - 4], }, slice.payload() ); diff --git a/etherparse/src/link/lax_ether_payload_slice.rs b/etherparse/src/link/lax_ether_payload_slice.rs new file mode 100644 index 00000000..27e4c02c --- /dev/null +++ b/etherparse/src/link/lax_ether_payload_slice.rs @@ -0,0 +1,22 @@ +use crate::*; + +/// Laxly identified payload of an link layer packet (potentially incomplete). +/// +/// To check if the payload is complete check the `incomplete` field. +#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub struct LaxEtherPayloadSlice<'a> { + /// True if the length field in the link header (e.g. MACsec short length) + /// indicates more data should be present but it was not (aka the packet + /// data is cut off). + pub incomplete: bool, + + /// Identifying content of the payload. + pub ether_type: EtherType, + + /// Length field that was used to determine the length + /// of the payload (e.g. MACsec "short length" field). + pub len_source: LenSource, + + /// Payload + pub payload: &'a [u8], +} diff --git a/etherparse/src/link/lax_link_ext_slice.rs b/etherparse/src/link/lax_link_ext_slice.rs new file mode 100644 index 00000000..08d95181 --- /dev/null +++ b/etherparse/src/link/lax_link_ext_slice.rs @@ -0,0 +1,228 @@ +use crate::*; + +/// A slice containing the link layer extension header (currently only Ethernet II and +/// SLL are supported). +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum LaxLinkExtSlice<'a> { + /// Slice containing a VLAN header & payload. + Vlan(SingleVlanSlice<'a>), + + /// Slice containing MACsec header & payload. + Macsec(LaxMacsecSlice<'a>), +} + +impl<'a> LaxLinkExtSlice<'a> { + /// Returns the header length of the link extension. + pub fn header_len(&self) -> usize { + match self { + LaxLinkExtSlice::Vlan(s) => s.header_len(), + LaxLinkExtSlice::Macsec(m) => m.header.header_len(), + } + } + + /// Convert the header part of the link extension into a [`LinkExtHeader`]. + pub fn to_header(&self) -> LinkExtHeader { + match self { + LaxLinkExtSlice::Vlan(s) => LinkExtHeader::Vlan(s.to_header()), + LaxLinkExtSlice::Macsec(m) => LinkExtHeader::Macsec(m.header.to_header()), + } + } + + /// Return the payload of the link extensions. + pub fn payload(&self) -> Option> { + match self { + LaxLinkExtSlice::Vlan(s) => { + let p = s.payload(); + Some(LaxEtherPayloadSlice { + incomplete: false, + ether_type: p.ether_type, + len_source: p.len_source, + payload: p.payload, + }) + } + LaxLinkExtSlice::Macsec(m) => { + if let LaxMacsecPayloadSlice::Unmodified(p) = &m.payload { + Some(p.clone()) + } else { + None + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_gens::*; + use alloc::{format, vec::Vec}; + use proptest::prelude::*; + + proptest! { + #[test] + fn debug_clone_eq(ref vlan in vlan_single_any()) { + let bytes = vlan.to_bytes(); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Vlan( + e.clone() + ); + + // clone & eq + assert_eq!(slice.clone(), slice); + + // debug + assert_eq!( + format!("{:?}", slice), + format!("Vlan({:?})", e), + ); + } + } + + proptest! { + #[test] + fn header_len( + vlan in vlan_single_any(), + macsec in macsec_any(), + ) { + // vlan + { + let bytes = vlan.to_bytes(); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Vlan( + e.clone() + ); + assert_eq!(slice.header_len(), e.header_len()); + } + // macsec + { + let mut macsec = macsec.clone(); + macsec.short_len = MacsecShortLen::ZERO; + let bytes = macsec.to_bytes(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Macsec(m.clone()); + assert_eq!(slice.header_len(), macsec.header_len()); + } + } + } + + proptest! { + #[test] + fn to_header( + vlan in vlan_single_any(), + macsec in macsec_any(), + ) { + // vlan + { + let bytes = vlan.to_bytes(); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Vlan( + e.clone() + ); + assert_eq!(slice.to_header(), LinkExtHeader::Vlan(e.to_header())); + } + // macsec + { + let mut macsec = macsec.clone(); + macsec.short_len = MacsecShortLen::ZERO; + let bytes = macsec.to_bytes(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Macsec(m.clone()); + assert_eq!(slice.to_header(), LinkExtHeader::Macsec(macsec.clone())); + } + } + } + + proptest! { + #[test] + fn payload( + vlan in vlan_single_any(), + macsec in macsec_any(), + ethertype in ether_type_any(), + ) { + // vlan + { + let payload = [1,2,3,4]; + let mut bytes = Vec::with_capacity(SingleVlanHeader::LEN + 4); + bytes.extend_from_slice(&vlan.to_bytes()); + bytes.extend_from_slice(&payload); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Vlan( + e.clone() + ); + assert_eq!( + slice.payload(), + Some(LaxEtherPayloadSlice{ + incomplete: false, + ether_type: vlan.ether_type, + len_source: LenSource::Slice, + payload: &payload + }) + ); + } + // macsec (unmodified, complete) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.short_len = MacsecShortLen::try_from(8 + 2).unwrap(); + let payload = [1,2,3,4,5,6,7,8]; + let mut bytes = Vec::with_capacity(macsec.header_len() + 8); + bytes.extend_from_slice(&macsec.to_bytes()); + bytes.extend_from_slice(&payload); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Macsec( + m.clone() + ); + prop_assert_eq!( + slice.payload(), + Some(LaxEtherPayloadSlice{ + incomplete: false, + ether_type: ethertype, + len_source: LenSource::MacsecShortLength, + payload: &payload + }) + ); + } + // macsec (unmodified, incomplete) + { + let payload = [1,2,3,4,5,6,7]; + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.set_payload_len(payload.len() + 1); + let mut bytes = Vec::with_capacity(macsec.header_len() + payload.len()); + bytes.extend_from_slice(&macsec.to_bytes()); + bytes.extend_from_slice(&payload); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Macsec( + m.clone() + ); + prop_assert_eq!( + slice.payload(), + Some(LaxEtherPayloadSlice{ + incomplete: true, + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + } + // macsec (modified) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Modified; + macsec.short_len = MacsecShortLen::try_from(1).unwrap(); + let payload = [1,2,3,4,5,6,7,8]; + let mut bytes = Vec::with_capacity(macsec.header_len() + 8); + bytes.extend_from_slice(&macsec.to_bytes()); + bytes.extend_from_slice(&payload); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + let slice = LaxLinkExtSlice::Macsec( + m.clone() + ); + prop_assert_eq!( + slice.payload(), + None + ); + } + } + } +} diff --git a/etherparse/src/link/lax_macsec_payload_slice.rs b/etherparse/src/link/lax_macsec_payload_slice.rs new file mode 100644 index 00000000..63e087f3 --- /dev/null +++ b/etherparse/src/link/lax_macsec_payload_slice.rs @@ -0,0 +1,10 @@ +use crate::*; + +#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub enum LaxMacsecPayloadSlice<'a> { + /// Unencrypted unmodified ether payload. + Unmodified(LaxEtherPayloadSlice<'a>), + + /// Modified payload (either by encryption or other algorithm). + Modified { incomplete: bool, payload: &'a [u8] }, +} diff --git a/etherparse/src/link/lax_macsec_slice.rs b/etherparse/src/link/lax_macsec_slice.rs new file mode 100644 index 00000000..46952c94 --- /dev/null +++ b/etherparse/src/link/lax_macsec_slice.rs @@ -0,0 +1,306 @@ +use crate::{err::macsec, *}; + +/// MACsec packet (SecTag header & payload). +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LaxMacsecSlice<'a> { + pub header: MacsecHeaderSlice<'a>, + pub payload: LaxMacsecPayloadSlice<'a>, +} + +impl<'a> LaxMacsecSlice<'a> { + pub fn from_slice(slice: &'a [u8]) -> Result, macsec::HeaderSliceError> { + let header = MacsecHeaderSlice::from_slice(slice)?; + // validate the length of the slice if the short length is set + let (incomplete, payload_slice, len_source) = + if let Some(req_payload_len) = header.expected_payload_len() { + let required_len = header.header_len() + req_payload_len; + if slice.len() < required_len { + ( + true, + // SAFETY: Safe as the header is a subslice of the original slice. + unsafe { + core::slice::from_raw_parts( + slice.as_ptr().add(header.slice().len()), + slice.len() - header.slice().len(), + ) + }, + LenSource::Slice, + ) + } else { + ( + false, + // SAFETY: Safe as the length was verified above to be at least required_len + // and required_len contains header.slice().len(). + unsafe { + core::slice::from_raw_parts( + slice.as_ptr().add(header.slice().len()), + req_payload_len, + ) + }, + LenSource::MacsecShortLength, + ) + } + } else { + ( + false, + // SAFETY: Safe as the header is a subslice of the original slice. + unsafe { + core::slice::from_raw_parts( + slice.as_ptr().add(header.slice().len()), + slice.len() - header.slice().len(), + ) + }, + LenSource::Slice, + ) + }; + + let payload = if let Some(ether_type) = header.next_ether_type() { + LaxMacsecPayloadSlice::Unmodified(LaxEtherPayloadSlice { + incomplete, + ether_type, + len_source, + payload: payload_slice, + }) + } else { + LaxMacsecPayloadSlice::Modified { + incomplete, + payload: payload_slice, + } + }; + + Ok(LaxMacsecSlice { header, payload }) + } + + /// Get the ether payload if the macsec packet is unencrypted & unmodified. + pub fn ether_payload(&self) -> Option> { + if let LaxMacsecPayloadSlice::Unmodified(e) = &self.payload { + Some(e.clone()) + } else { + None + } + } + + /// Get the ether type of the payload if the macsec packet is unencrypted & unmodified. + pub fn next_ether_type(&self) -> Option { + self.header.next_ether_type() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{err::LenError, test_gens::*}; + use arrayvec::ArrayVec; + use proptest::prelude::*; + + proptest! { + #[test] + fn from_slice( + macsec in macsec_any(), + ethertype in ether_type_any(), + non_zero_sl_unmodified in 3u8..=0b0011_1111, + non_zero_sl_modified in 1u8..=0b0011_1111 + ) { + // macsec (unmodified, complete, nonzero short length) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_unmodified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..(non_zero_sl_unmodified - 2) { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + LaxMacsecPayloadSlice::Unmodified(LaxEtherPayloadSlice{ + incomplete: false, + ether_type: ethertype, + len_source: LenSource::MacsecShortLength, + payload: &payload + }) + ); + assert_eq!( + m.ether_payload(), + Some(LaxEtherPayloadSlice{ + incomplete: false, + ether_type: ethertype, + len_source: LenSource::MacsecShortLength, + payload: &payload + }) + ); + assert_eq!(m.next_ether_type(), Some(ethertype)); + } + // macsec (unmodified, incomplete, nonzero short length) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_unmodified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_unmodified-2-1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + LaxMacsecPayloadSlice::Unmodified(LaxEtherPayloadSlice{ + incomplete: true, + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + assert_eq!( + m.ether_payload(), + Some(LaxEtherPayloadSlice{ + incomplete: true, + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + assert_eq!(m.next_ether_type(), Some(ethertype)); + } + // macsec (unmodified, zero short length) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.short_len = MacsecShortLen::ZERO; + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_unmodified+1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + LaxMacsecPayloadSlice::Unmodified(LaxEtherPayloadSlice{ + incomplete: false, + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + assert_eq!( + m.ether_payload(), + Some(LaxEtherPayloadSlice{ + incomplete: false, + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + assert_eq!(m.next_ether_type(), Some(ethertype)); + } + // macsec (modified, complete, nonzero short length) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_modified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_modified { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + LaxMacsecPayloadSlice::Modified{ + incomplete: false, + payload: &payload, + } + ); + assert_eq!(m.ether_payload(), None); + assert_eq!(m.next_ether_type(), None); + } + // macsec (modified, incomplete, nonzero short length) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_modified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_modified-1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + LaxMacsecPayloadSlice::Modified{ + incomplete: true, + payload: &payload, + } + ); + assert_eq!(m.ether_payload(), None); + assert_eq!(m.next_ether_type(), None); + } + // macsec (modified, zero short length) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::ZERO; + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_modified+1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + LaxMacsecPayloadSlice::Modified{ + incomplete: false, + payload: &payload, + } + ); + assert_eq!(m.ether_payload(), None); + assert_eq!(m.next_ether_type(), None); + } + // header parse error + for ptype in [MacsecPType::Unmodified(ethertype), MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::ZERO; + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + let m = LaxMacsecSlice::from_slice(&bytes[..bytes.len() - 1]); + assert_eq!( + m, + Err(macsec::HeaderSliceError::Len(LenError{ + required_len: macsec.header_len(), + len: macsec.header_len() - 1, + len_source: LenSource::Slice, + layer: err::Layer::MacsecHeader, + layer_start_offset: 0 + })) + ); + } + } + } +} diff --git a/etherparse/src/link/link_ext_header.rs b/etherparse/src/link/link_ext_header.rs new file mode 100644 index 00000000..54bbb4a6 --- /dev/null +++ b/etherparse/src/link/link_ext_header.rs @@ -0,0 +1,65 @@ +use super::*; + +/// The possible headers on the link layer +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum LinkExtHeader { + /// VLAN header. + Vlan(SingleVlanHeader), + + /// MACsec header (SecTag + next ether type if available). + Macsec(MacsecHeader), +} + +impl LinkExtHeader { + /// Returns the header length of the link extension. + pub fn header_len(&self) -> usize { + match self { + LinkExtHeader::Vlan(s) => s.header_len(), + LinkExtHeader::Macsec(m) => m.header_len(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_gens::*; + use alloc::format; + use proptest::prelude::*; + + proptest! { + #[test] + fn debug_clone_eq(ref vlan in vlan_single_any()) { + let header = LinkExtHeader::Vlan(vlan.clone()); + + // clone & eq + assert_eq!(header.clone(), header); + + // debug + assert_eq!( + format!("{:?}", header), + format!("Vlan({:?})", vlan), + ); + } + } + + proptest! { + #[test] + fn header_len( + vlan in vlan_single_any(), + macsec in macsec_any() + ) { + // vlan + { + let header = LinkExtHeader::Vlan(vlan.clone()); + assert_eq!(header.header_len(), vlan.header_len()); + } + + // macsec + { + let header = LinkExtHeader::Macsec(macsec.clone()); + assert_eq!(header.header_len(), macsec.header_len()); + } + } + } +} diff --git a/etherparse/src/link/link_ext_slice.rs b/etherparse/src/link/link_ext_slice.rs new file mode 100644 index 00000000..dbf76f24 --- /dev/null +++ b/etherparse/src/link/link_ext_slice.rs @@ -0,0 +1,199 @@ +use crate::*; + +/// A slice containing the link layer extension header (currently only Ethernet II and +/// SLL are supported). +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum LinkExtSlice<'a> { + /// Slice containing a VLAN header & payload. + Vlan(SingleVlanSlice<'a>), + + /// Slice containing MACsec header & payload. + Macsec(MacsecSlice<'a>), +} + +impl<'a> LinkExtSlice<'a> { + /// Returns the header length of the link extension. + pub fn header_len(&self) -> usize { + match self { + LinkExtSlice::Vlan(s) => s.header_len(), + LinkExtSlice::Macsec(m) => m.header.header_len(), + } + } + + /// Convert the header part of the link extension into a [`LinkExtHeader`]. + pub fn to_header(&self) -> LinkExtHeader { + match self { + LinkExtSlice::Vlan(s) => LinkExtHeader::Vlan(s.to_header()), + LinkExtSlice::Macsec(m) => LinkExtHeader::Macsec(m.header.to_header()), + } + } + + /// Return the payload of the link extensions. + pub fn ether_payload(&self) -> Option> { + match self { + LinkExtSlice::Vlan(s) => Some(s.payload()), + LinkExtSlice::Macsec(m) => { + if let MacsecPayloadSlice::Unmodified(p) = &m.payload { + Some(p.clone()) + } else { + None + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_gens::*; + use alloc::{format, vec::Vec}; + use arrayvec::ArrayVec; + use proptest::prelude::*; + + proptest! { + #[test] + fn debug_clone_eq(ref vlan in vlan_single_any()) { + let bytes = vlan.to_bytes(); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Vlan( + e.clone() + ); + + // clone & eq + assert_eq!(slice.clone(), slice); + + // debug + assert_eq!( + format!("{:?}", slice), + format!("Vlan({:?})", e), + ); + } + } + + proptest! { + #[test] + fn header_len( + vlan in vlan_single_any(), + macsec in macsec_any() + ) { + // vlan + { + let bytes = vlan.to_bytes(); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Vlan( + e.clone() + ); + assert_eq!(slice.header_len(), e.header_len()); + } + // macsec + { + let mut macsec = macsec.clone(); + macsec.short_len = MacsecShortLen::ZERO; + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + let e = MacsecSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Macsec( + e.clone() + ); + assert_eq!(slice.header_len(), macsec.header_len()); + } + } + } + + proptest! { + #[test] + fn to_header( + vlan in vlan_single_any(), + macsec in macsec_any() + ) { + // vlan + { + let bytes = vlan.to_bytes(); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Vlan( + e.clone() + ); + assert_eq!(slice.to_header(), LinkExtHeader::Vlan(e.to_header())); + } + // macsec + { + let mut macsec = macsec.clone(); + macsec.short_len = MacsecShortLen::ZERO; + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + let e = MacsecSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Macsec( + e.clone() + ); + assert_eq!(slice.to_header(), LinkExtHeader::Macsec(macsec.clone())); + } + } + } + + proptest! { + #[test] + fn ether_payload( + vlan in vlan_single_any(), + macsec in macsec_any(), + ether_type in ether_type_any(), + ) { + // vlan + { + let payload = [1,2,3,4]; + let mut bytes = Vec::with_capacity(SingleVlanHeader::LEN + 4); + bytes.extend_from_slice(&vlan.to_bytes()); + bytes.extend_from_slice(&payload); + let e = SingleVlanSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Vlan( + e.clone() + ); + assert_eq!( + slice.ether_payload(), + Some(EtherPayloadSlice{ + ether_type: vlan.ether_type, + len_source: LenSource::Slice, + payload: &payload + }) + ); + } + // macsec (unmodified) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ether_type); + macsec.short_len = MacsecShortLen::ZERO; + let payload = [1,2,3,4]; + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let e = MacsecSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Macsec( + e.clone() + ); + assert_eq!( + slice.ether_payload(), + Some(EtherPayloadSlice{ + ether_type, + len_source: LenSource::Slice, + payload: &payload, + }) + ); + } + // macsec (modified) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::ZERO; + let payload = [1,2,3,4]; + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let e = MacsecSlice::from_slice(&bytes).unwrap(); + let slice = LinkExtSlice::Macsec( + e.clone() + ); + assert_eq!(slice.ether_payload(), None); + } + } + } +} diff --git a/etherparse/src/link/link_slice.rs b/etherparse/src/link/link_slice.rs index fb1bd031..52d248a5 100644 --- a/etherparse/src/link/link_slice.rs +++ b/etherparse/src/link/link_slice.rs @@ -108,6 +108,7 @@ mod test { { let slice = LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: ether_type::IPV4, + len_source: LenSource::Slice, payload: &[] }); assert_eq!( @@ -144,17 +145,26 @@ mod test { ); assert_eq!( slice.ether_payload().unwrap(), - EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p } + EtherPayloadSlice{ + ether_type: eth.ether_type, + len_source: LenSource::Slice, + payload: &p + } ); } { let slice = LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: eth.ether_type, + len_source: LenSource::Slice, payload: &p }); assert_eq!( slice.ether_payload().unwrap(), - EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p } + EtherPayloadSlice{ + ether_type: eth.ether_type, + len_source: LenSource::Slice, + payload: &p + } ); } { @@ -167,7 +177,11 @@ mod test { match linux_sll.protocol_type { LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => { assert_eq!( slice.ether_payload().unwrap(), - EtherPayloadSlice{ ether_type: EtherType(v), payload: &p } + EtherPayloadSlice{ + ether_type: EtherType(v), + len_source: LenSource::Slice, + payload: &p, + } );} _ => { assert!(slice.ether_payload().is_none());} } @@ -180,7 +194,11 @@ mod test { match linux_sll.protocol_type { LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => { assert_eq!( slice.ether_payload().unwrap(), - EtherPayloadSlice{ ether_type: EtherType(v), payload: &p } + EtherPayloadSlice{ + ether_type: EtherType(v), + len_source: LenSource::Slice, + payload: &p, + } );} _ => { assert!(slice.ether_payload().is_none());} } @@ -213,6 +231,7 @@ mod test { { let slice = LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: eth.ether_type, + len_source: LenSource::Slice, payload: &p }); assert_eq!( diff --git a/etherparse/src/link/linux_sll_header.rs b/etherparse/src/link/linux_sll_header.rs index ec1a1ba9..c27aa440 100644 --- a/etherparse/src/link/linux_sll_header.rs +++ b/etherparse/src/link/linux_sll_header.rs @@ -7,9 +7,9 @@ pub struct LinuxSllHeader { pub packet_type: LinuxSllPacketType, /// ARPHRD_ value for the link-layer device type pub arp_hrd_type: ArpHardwareId, - /// The size of the adress that is valid + /// The size of the address that is valid pub sender_address_valid_length: u16, - /// The link-layer adress of the sender of the packet, with the meaningful + /// The link-layer address of the sender of the packet, with the meaningful /// bytes specified by `sender_address_valid_length`. If the original is /// larger, the value on the packet is truncated to the first 8 bytes. If /// the original is smaller, the remaining bytes will be filled with 0s. diff --git a/etherparse/src/link/linux_sll_header_slice.rs b/etherparse/src/link/linux_sll_header_slice.rs index 274cbab0..dbf96da7 100644 --- a/etherparse/src/link/linux_sll_header_slice.rs +++ b/etherparse/src/link/linux_sll_header_slice.rs @@ -87,7 +87,7 @@ impl<'a> LinuxSllHeaderSlice<'a> { #[inline] pub fn packet_type(&self) -> LinuxSllPacketType { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of LinuxSllHeader::LEN (16). let packet_type_raw = unsafe { get_unchecked_be_u16(self.slice.as_ptr()) }; @@ -100,7 +100,7 @@ impl<'a> LinuxSllHeaderSlice<'a> { #[inline] pub fn arp_hardware_type(&self) -> ArpHardwareId { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of LinuxSllHeader::LEN (16). let arp_hardware_type_raw = unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }; @@ -111,7 +111,7 @@ impl<'a> LinuxSllHeaderSlice<'a> { #[inline] pub fn sender_address_valid_length(&self) -> u16 { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of LinuxSllHeader::LEN (16). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) } } @@ -121,7 +121,7 @@ impl<'a> LinuxSllHeaderSlice<'a> { #[inline] pub fn sender_address_full(&self) -> [u8; 8] { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of LinuxSllHeader::LEN (16). unsafe { get_unchecked_8_byte_array(self.slice.as_ptr().add(6)) } } @@ -136,16 +136,17 @@ impl<'a> LinuxSllHeaderSlice<'a> { /// Read the protocol type field #[inline] pub fn protocol_type(&self) -> LinuxSllProtocolType { - let arp_harware_type = self.arp_hardware_type(); + let arp_hardware_type = self.arp_hardware_type(); // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of LinuxSllHeader::LEN (16). let protocol_type_raw = unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(14)) }; // SAFETY: - // Safe as the constructor checks that the arphwd + protocol are supported + // Safe as the constructor checks that the arphw + protocol are supported unsafe { - LinuxSllProtocolType::try_from((arp_harware_type, protocol_type_raw)).unwrap_unchecked() + LinuxSllProtocolType::try_from((arp_hardware_type, protocol_type_raw)) + .unwrap_unchecked() } } @@ -172,12 +173,26 @@ mod test { #[test] fn from_slice( input in linux_sll_any(), - dummy_data in proptest::collection::vec(any::(), 0..20) + dummy_data in proptest::collection::vec(any::(), 0..20), + bad_packet_type in LinuxSllPacketType::MAX_VAL + 1..=u16::MAX, + bad_hw_type in any::().prop_filter( + "hw id must be unknown", + |v| ![ + ArpHardwareId::NETLINK, + ArpHardwareId::IPGRE, + ArpHardwareId::IEEE80211_RADIOTAP, + ArpHardwareId::FRAD, + ArpHardwareId::ETHERNET, + ].iter().any(|&x| *v == x.0) + ) ) { // serialize - let mut buffer: Vec = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len()); - input.write(&mut buffer).unwrap(); - buffer.extend(&dummy_data[..]); + let buffer = { + let mut buffer: Vec = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len()); + input.write(&mut buffer).unwrap(); + buffer.extend(&dummy_data[..]); + buffer + }; // calls with a valid result { @@ -198,6 +213,34 @@ mod test { })) ); } + + // packet_type_val error + { + let mut modbuf = buffer.clone(); + let p_be = bad_packet_type.to_be_bytes(); + modbuf[0] = p_be[0]; + modbuf[1] = p_be[1]; + assert_eq!( + LinuxSllHeaderSlice::from_slice(&modbuf), + Err(err::linux_sll::HeaderSliceError::Content( + err::linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: bad_packet_type } + )) + ); + } + + // hardware_id error + { + let mut modbuf = buffer.clone(); + let p_be = bad_hw_type.to_be_bytes(); + modbuf[2] = p_be[0]; + modbuf[3] = p_be[1]; + assert_eq!( + LinuxSllHeaderSlice::from_slice(&modbuf), + Err(err::linux_sll::HeaderSliceError::Content( + err::linux_sll::HeaderError::UnsupportedArpHardwareId { arp_hardware_type: ArpHardwareId(bad_hw_type) } + )) + ); + } } } @@ -210,6 +253,7 @@ mod test { assert_eq!(input.arp_hrd_type, slice.arp_hardware_type()); assert_eq!(input.sender_address_valid_length, slice.sender_address_valid_length()); assert_eq!(input.sender_address, slice.sender_address_full()); + assert_eq!(&input.sender_address[..usize::from(input.sender_address_valid_length)], slice.sender_address()); assert_eq!(input.protocol_type, slice.protocol_type()); } } diff --git a/etherparse/src/link/linux_sll_payload_slice.rs b/etherparse/src/link/linux_sll_payload_slice.rs index 3c39c2a9..6b168c91 100644 --- a/etherparse/src/link/linux_sll_payload_slice.rs +++ b/etherparse/src/link/linux_sll_payload_slice.rs @@ -1,4 +1,4 @@ -use crate::{EtherPayloadSlice, EtherType, LinuxSllProtocolType}; +use crate::{EtherPayloadSlice, EtherType, LenSource, LinuxSllProtocolType}; /// Payload of Linux Cooked Capture v1 (SLL) packet #[derive(Clone, Debug, Eq, PartialEq)] @@ -27,11 +27,13 @@ impl<'a> TryFrom> for EtherPayloadSlice<'a> { LinuxSllProtocolType::LinuxNonstandardEtherType(nonstandard_ether_type) => { Ok(EtherPayloadSlice { ether_type: EtherType(nonstandard_ether_type.into()), + len_source: LenSource::Slice, payload: value.payload, }) } LinuxSllProtocolType::EtherType(ether_type) => Ok(EtherPayloadSlice { ether_type, + len_source: LenSource::Slice, payload: value.payload, }), _ => Err(()), diff --git a/etherparse/src/link/linux_sll_protocol_type.rs b/etherparse/src/link/linux_sll_protocol_type.rs index 7ee53c29..12432932 100644 --- a/etherparse/src/link/linux_sll_protocol_type.rs +++ b/etherparse/src/link/linux_sll_protocol_type.rs @@ -1,6 +1,6 @@ use crate::{err, ArpHardwareId, EtherType, LinuxNonstandardEtherType}; -/// Represents the "protcol type" field in a Linux Cooked Capture v1 packet. It +/// Represents the "protocol type" field in a Linux Cooked Capture v1 packet. It /// is represented as an enum due to the meaning of the inner value depending /// on the associated arp_hardware_id field. /// diff --git a/etherparse/src/link/linux_sll_slice.rs b/etherparse/src/link/linux_sll_slice.rs index 3adb34ea..cd069c0c 100644 --- a/etherparse/src/link/linux_sll_slice.rs +++ b/etherparse/src/link/linux_sll_slice.rs @@ -193,6 +193,7 @@ mod test { assert_eq!(linux_sll.arp_hrd_type, slice.arp_hardware_type()); assert_eq!(linux_sll.sender_address_valid_length, slice.sender_address_valid_length()); assert_eq!(linux_sll.sender_address, slice.sender_address_full()); + assert_eq!(&linux_sll.sender_address[..usize::from(linux_sll.sender_address_valid_length)], slice.sender_address()); assert_eq!(linux_sll.protocol_type, slice.protocol_type()); assert_eq!(&payload, slice.payload_slice()); assert_eq!( diff --git a/etherparse/src/link/macsec_an.rs b/etherparse/src/link/macsec_an.rs new file mode 100644 index 00000000..639982e9 --- /dev/null +++ b/etherparse/src/link/macsec_an.rs @@ -0,0 +1,254 @@ +use crate::err::ValueTooBigError; + +/// 2 bit unsigned integer containing the "MACsec association number". +/// (present in the [`crate::MacSecHeader`]). +/// +/// Identifies up to four SAs within the context of an SC. +#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct MacsecAn(u8); + +impl MacsecAn { + /// MacsecAn with value 0. + pub const ZERO: MacsecAn = MacsecAn(0); + + /// Maximum value of a "MACsec association number". + pub const MAX_U8: u8 = 0b0000_0011; + + /// Tries to create an [`MacsecAn`] and checks that the passed value + /// is smaller or equal than [`MacsecAn::MAX_U8`] (2 bit unsigned integer). + /// + /// In case the passed value is bigger then what can be represented in an 2 bit + /// integer an error is returned. Otherwise an `Ok` containing the [`MacsecAn`]. + /// + /// ``` + /// use etherparse::MacsecAn; + /// + /// let an = MacsecAn::try_new(2).unwrap(); + /// assert_eq!(an.value(), 2); + /// + /// // if a number that can not be represented in an 2 bit integer + /// // gets passed in an error is returned + /// use etherparse::err::{ValueTooBigError, ValueType}; + /// assert_eq!( + /// MacsecAn::try_new(MacsecAn::MAX_U8 + 1), + /// Err(ValueTooBigError{ + /// actual: MacsecAn::MAX_U8 + 1, + /// max_allowed: MacsecAn::MAX_U8, + /// value_type: ValueType::MacsecAn, + /// }) + /// ); + /// ``` + #[inline] + pub const fn try_new(value: u8) -> Result> { + use crate::err::ValueType; + if value <= MacsecAn::MAX_U8 { + Ok(MacsecAn(value)) + } else { + Err(ValueTooBigError { + actual: value, + max_allowed: MacsecAn::MAX_U8, + value_type: ValueType::MacsecAn, + }) + } + } + + /// Creates an [`MacsecAn`] without checking that the value + /// is smaller or equal than [`MacsecAn::MAX_U8`] (2 bit unsigned integer). + /// The caller must guarantee that `value <= MacsecAn::MAX_U8`. + /// + /// # Safety + /// + /// `value` must be smaller or equal than [`MacsecAn::MAX_U8`] + /// otherwise the behavior of functions or data structures relying + /// on this pre-requirement is undefined. + #[inline] + pub const unsafe fn new_unchecked(value: u8) -> MacsecAn { + debug_assert!(value <= MacsecAn::MAX_U8); + MacsecAn(value) + } + + /// Returns the underlying unsigned 2 bit value as an `u8` value. + #[inline] + pub const fn value(self) -> u8 { + self.0 + } +} + +impl core::fmt::Display for MacsecAn { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } +} + +impl From for u8 { + #[inline] + fn from(value: MacsecAn) -> Self { + value.0 + } +} + +impl TryFrom for MacsecAn { + type Error = ValueTooBigError; + + #[inline] + fn try_from(value: u8) -> Result { + use crate::err::ValueType; + if value <= MacsecAn::MAX_U8 { + Ok(MacsecAn(value)) + } else { + Err(Self::Error { + actual: value, + max_allowed: MacsecAn::MAX_U8, + value_type: ValueType::MacsecAn, + }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use core::hash::{Hash, Hasher}; + use proptest::prelude::*; + use std::format; + + #[test] + fn derived_traits() { + // copy & clone + { + let a = MacsecAn(2); + let b = a; + assert_eq!(a, b); + assert_eq!(a.clone(), a); + } + + // default + { + let actual: MacsecAn = Default::default(); + assert_eq!(actual.value(), 0); + } + + // debug + { + let a = MacsecAn(2); + assert_eq!(format!("{:?}", a), format!("MacsecAn(2)")); + } + + // ord & partial ord + { + use core::cmp::Ordering; + let a = MacsecAn(2); + let b = a; + assert_eq!(a.cmp(&b), Ordering::Equal); + assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal)); + } + + // hash + { + use std::collections::hash_map::DefaultHasher; + let a = { + let mut hasher = DefaultHasher::new(); + MacsecAn(2).hash(&mut hasher); + hasher.finish() + }; + let b = { + let mut hasher = DefaultHasher::new(); + MacsecAn(2).hash(&mut hasher); + hasher.finish() + }; + assert_eq!(a, b); + } + } + + proptest! { + #[test] + fn try_new( + valid_value in 0..=0b0000_0011u8, + invalid_value in 0b0000_0100u8..=u8::MAX + ) { + use crate::err::{ValueType, ValueTooBigError}; + assert_eq!( + valid_value, + MacsecAn::try_new(valid_value).unwrap().value() + ); + assert_eq!( + MacsecAn::try_new(invalid_value).unwrap_err(), + ValueTooBigError{ + actual: invalid_value, + max_allowed: 0b0000_0011, + value_type: ValueType::MacsecAn + } + ); + } + } + + proptest! { + #[test] + fn try_from( + valid_value in 0..=0b0000_0011u8, + invalid_value in 0b0000_0100u8..=u8::MAX + ) { + use crate::err::{ValueType, ValueTooBigError}; + // try_into + { + let actual: MacsecAn = valid_value.try_into().unwrap(); + assert_eq!(actual.value(), valid_value); + + let err: Result> = invalid_value.try_into(); + assert_eq!( + err.unwrap_err(), + ValueTooBigError{ + actual: invalid_value, + max_allowed: 0b0000_0011, + value_type: ValueType::MacsecAn + } + ); + } + // try_from + { + assert_eq!( + MacsecAn::try_from(valid_value).unwrap().value(), + valid_value + ); + + assert_eq!( + MacsecAn::try_from(invalid_value).unwrap_err(), + ValueTooBigError{ + actual: invalid_value, + max_allowed: 0b0000_0011, + value_type: ValueType::MacsecAn + } + ); + } + } + } + + proptest! { + #[test] + fn new_unchecked(valid_value in 0..=0b0000_0011u8) { + assert_eq!( + valid_value, + unsafe { + MacsecAn::new_unchecked(valid_value).value() + } + ); + } + } + + proptest! { + #[test] + fn fmt(valid_value in 0..=0b0000_0011u8) { + assert_eq!(format!("{}", MacsecAn(valid_value)), format!("{}", valid_value)); + } + } + + proptest! { + #[test] + fn from(valid_value in 0..=0b0000_0011u8,) { + let pcp = MacsecAn::try_new(valid_value).unwrap(); + let actual: u8 = pcp.into(); + assert_eq!(actual, valid_value); + } + } +} diff --git a/etherparse/src/link/macsec_header.rs b/etherparse/src/link/macsec_header.rs new file mode 100644 index 00000000..268c3adf --- /dev/null +++ b/etherparse/src/link/macsec_header.rs @@ -0,0 +1,531 @@ +use crate::*; +use arrayvec::ArrayVec; + +/// MACsec SecTag header (present at the start of a +/// packet capsuled with MACsec). +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MacsecHeader { + /// Payload type (contains encryption, modification flag as + /// well as the next ether type if available) + pub ptype: MacsecPType, + + /// End station identifier (TCI.ES flag). + pub endstation_id: bool, + + /// Ethernet passive optical network broadcast flag. + pub scb: bool, + + /// Association number (identifies SAs). + pub an: MacsecAn, + + /// Short length with reserved bits. + pub short_len: MacsecShortLen, + + /// Packet number. + pub packet_nr: u32, + + /// Secure channel identifier. + pub sci: Option, +} + +impl MacsecHeader { + /// Minimum length of an MacSec header in bytes/octets. + pub const MIN_LEN: usize = 6; + + /// Maximum length of an MacSec header (including ether type of payload) in bytes/octets. + pub const MAX_LEN: usize = 16; + + /// Encryption flag, which indicates whether the user data is + /// encrypted (true = encrypted, TCI.E flag). + #[inline] + pub fn encrypted(&self) -> bool { + use MacsecPType::*; + matches!(self.ptype, Encrypted | EncryptedUnmodified) + } + + /// Flag for change text, set if the user data is modified. + pub fn userdata_changed(&self) -> bool { + use MacsecPType::*; + matches!(self.ptype, Encrypted | Modified) + } + + /// Ether type of the data following the mac sec tag. + pub fn next_ether_type(&self) -> Option { + if let MacsecPType::Unmodified(re) = self.ptype { + Some(re) + } else { + None + } + } + + /// Try creating a [`MacSecHeaderSlice`] from a slice containing the + /// MACsec header & next ether type. + pub fn from_slice(slice: &[u8]) -> Result { + MacsecHeaderSlice::from_slice(slice).map(|v| v.to_header()) + } + + /// Serialize the mac sec header. + pub fn to_bytes(&self) -> ArrayVec { + // tci-an is composed of: + // --------------------------------------- + // | v | es | sc | scp | e | c | an | an | + // --------------------------------------- + // bits 8 7 6 5 4 3 2 1 + // + // - version (0) + // - es (end station identifier bit) + // - sc (SCI present bit) + // - scp (Ethernet passive optical network broadcast bit) + // - e (encryption bit) + // - c (user data change bit) + // - an (Association number) [2 bits] + let tci_an = (self.an.value() & 0b11) + | if self.userdata_changed() { 0b100 } else { 0 } + | if self.encrypted() { 0b1000 } else { 0 } + | if self.scb { 0b1_0000 } else { 0 } + | if self.sci.is_some() { 0b10_0000 } else { 0 } + | if self.endstation_id { 0b100_0000 } else { 0 }; + let pn_be = self.packet_nr.to_be_bytes(); + let sci_be = self.sci.unwrap_or(0).to_be_bytes(); + let et_be = if let MacsecPType::Unmodified(e) = self.ptype { + e.0 + } else { + 0 + } + .to_be_bytes(); + let mut result: ArrayVec = if self.sci.is_some() { + [ + tci_an, + self.short_len.value() & 0b0011_1111, + pn_be[0], + pn_be[1], + pn_be[2], + pn_be[3], + sci_be[0], + sci_be[1], + sci_be[2], + sci_be[3], + sci_be[4], + sci_be[5], + sci_be[6], + sci_be[7], + et_be[0], + et_be[1], + ] + } else { + [ + tci_an, + self.short_len.value() & 0b0011_1111, + pn_be[0], + pn_be[1], + pn_be[2], + pn_be[3], + et_be[0], + et_be[1], + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ] + } + .into(); + // SAFETY: Safe as the maximum size of 16 can not be exceeded. + unsafe { + result.set_len( + 6 + if self.sci.is_some() { 8 } else { 0 } + + if matches!(self.ptype, MacsecPType::Unmodified(_)) { + 2 + } else { + 0 + }, + ); + } + result + } + + /// Try reading a MACsec header from the position of the reader. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub fn read( + reader: &mut T, + ) -> Result { + use err::macsec::HeaderError::*; + use err::macsec::HeaderReadError::*; + + let mut bytes = [0; MacsecHeader::MAX_LEN]; + reader.read_exact(&mut bytes[..6]).map_err(Io)?; + + // check version bit + let tci_an = bytes[0]; + if 0 != tci_an & 0b1000_0000 { + return Err(Content(UnexpectedVersion)); + } + + // validate short_len is not 1 in the unmodified case + let unmodified = 0 == tci_an & 0b1100; + if unmodified { + // SAFETY: Safe as the length was verified to be at least 6. + let short_len = bytes[1] & 0b0011_1111; + // short len must be zero (unknown) or at least 2 in case of an + // unmodified payload + if short_len == 1 { + return Err(Content(InvalidUnmodifiedShortLen)); + } + } + + // get the encrypted, changed flag (check if ether_type can be parsed) + let required_len = + 6 + if unmodified { 2 } else { 0 } + if 0 != tci_an & 0b10_0000 { 8 } else { 0 }; + + if required_len > 6 { + reader.read_exact(&mut bytes[6..required_len]).map_err(Io)?; + } + + Ok(MacsecHeaderSlice { + slice: &bytes[..required_len], + } + .to_header()) + } + + /// Writes a given MACsec header to the current position (SecTag & next + /// ether type if available). + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { + writer.write_all(&self.to_bytes()) + } + + /// Length of the MACsec header (SecTag + next ether type if available). + #[inline] + pub fn header_len(&self) -> usize { + 6 + if self.sci.is_some() { 8 } else { 0 } + + if matches!(self.ptype, MacsecPType::Unmodified(_)) { + 2 + } else { + 0 + } + } + + /// Returns the required length of the payload (data after header + + /// next_ether_type if present) if possible. + /// + /// If the length cannot be determined (`short_len` is zero or less then + /// `2` when `ptype` `Unmodified`) `None` is returned. + #[inline] + pub fn expected_payload_len(&self) -> Option { + let sl = self.short_len.value() as usize; + if sl > 0 { + if matches!(self.ptype, MacsecPType::Unmodified(_)) { + if sl < 2 { + None + } else { + Some(sl - 2) + } + } else { + // no ether type (encrypted and/or modified payload) + Some(sl) + } + } else { + None + } + } + + /// Set the `short_len` field based on the given payload byte len + /// (payload len excluding the ether_type if `ptype` `Unmodified`) + /// based on the current `ptype`. + #[inline] + pub fn set_payload_len(&mut self, payload_len: usize) { + if matches!(self.ptype, MacsecPType::Unmodified(_)) { + if payload_len > MacsecShortLen::MAX_USIZE - 2 { + self.short_len = MacsecShortLen::ZERO; + } else { + // SAFETY: Safe as payload_len + 2 <= MacsecShortLen::MAX_USIZE + // is guaranteed after the if above. + self.short_len = + unsafe { MacsecShortLen::from_u8_unchecked(payload_len as u8 + 2) }; + } + } else if payload_len > MacsecShortLen::MAX_USIZE { + self.short_len = MacsecShortLen::ZERO; + } else { + // SAFETY: Safe as payload_len + 2 <= MacsecShortLen::MAX_USIZE + // is guaranteed after the if above. + self.short_len = unsafe { MacsecShortLen::from_u8_unchecked(payload_len as u8) }; + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_gens::*; + use proptest::prelude::*; + use std::io::Cursor; + + proptest! { + #[test] + fn from_slice_to_bytes( + header in macsec_any() + ) { + let mut header = header.clone(); + if matches!(header.ptype, MacsecPType::Unmodified(_)) && header.short_len.value() == 1 { + header.short_len = MacsecShortLen::ZERO; + } + let bytes = header.to_bytes(); + let actual = MacsecHeader::from_slice(&bytes); + assert_eq!(actual, Ok(header.clone())); + } + } + + proptest! { + #[test] + fn getter( + macsec in macsec_any(), + ethertype in ether_type_any(), + ) { + + let tests = [ + // ptype, encrypted, userdata_changed, next_ether_type + (MacsecPType::Unmodified(ethertype), false, false, Some(ethertype)), + (MacsecPType::Modified, false, true, None), + (MacsecPType::Encrypted, true, true, None), + (MacsecPType::EncryptedUnmodified, true, false, None), + ]; + + for test in tests { + let mut macsec = macsec.clone(); + macsec.ptype = test.0; + + assert_eq!(test.1, macsec.encrypted()); + assert_eq!(test.2, macsec.userdata_changed()); + assert_eq!(test.3, macsec.next_ether_type()); + } + } + } + + proptest! { + #[test] + fn header_len( + macsec in macsec_any(), + ethertype in ether_type_any(), + sci in any::(), + ) { + // no ethertype + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + // no sci + { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.sci = None; + assert_eq!(6, macsec.header_len()); + } + // with sci + { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.sci = Some(sci); + assert_eq!(14, macsec.header_len()); + } + } + + // with ethertype + // no sci + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.sci = None; + assert_eq!(8, macsec.header_len()); + } + // with sci + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.sci = Some(sci); + assert_eq!(16, macsec.header_len()); + } + } + } + + proptest! { + #[test] + fn read( + macsec in macsec_any(), + ether_type in ether_type_any(), + sci in any::() + ) { + use MacsecPType::*; + use err::macsec::*; + + // variants + for ptype in [Unmodified(ether_type), Modified, Encrypted, EncryptedUnmodified] { + for has_sci in [false, true] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.sci = if has_sci { + Some(sci) + } else { + None + }; + if matches!(ptype, MacsecPType::Unmodified(_)) && macsec.short_len.value() == 1 { + macsec.short_len = MacsecShortLen::ZERO; + } + + // ok case + { + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + let mut cursor = Cursor::new(&bytes); + let m = MacsecHeader::read(&mut cursor).unwrap(); + assert_eq!(m, macsec); + assert_eq!(macsec.header_len() as u64, cursor.position()); + } + + // version error + { + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&[1]).unwrap(); + + // version bit + bytes[0] = bytes[0] | 0b1000_0000; + + let mut cursor = Cursor::new(&bytes); + let m = MacsecHeader::read(&mut cursor).unwrap_err(); + assert_eq!(m.content_error(), Some(HeaderError::UnexpectedVersion)); + } + + // short len error + if matches!(ptype, MacsecPType::Unmodified(_)) { + let mut macsec = macsec.clone(); + macsec.short_len = MacsecShortLen::try_from_u8(1).unwrap(); + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + + let mut cursor = Cursor::new(&bytes); + let m = MacsecHeader::read(&mut cursor).unwrap_err(); + assert_eq!(m.content_error(), Some(HeaderError::InvalidUnmodifiedShortLen)); + } + + // len error + for len in 0..macsec.header_len() { + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + + let mut cursor = Cursor::new(&bytes[..len]); + let m = MacsecHeader::read(&mut cursor); + assert!(m.unwrap_err().io_error().is_some()); + } + } + } + } + } + + proptest! { + #[test] + fn write( + header in macsec_any() + ) { + // ok case + { + let mut buffer = ArrayVec::::new(); + header.write(&mut buffer).unwrap(); + assert_eq!(&buffer, &header.to_bytes()); + } + // not enough memory + { + let mut buffer = [0u8;MacsecHeader::MAX_LEN]; + let mut cursor = Cursor::new(&mut buffer[..header.header_len() - 1]); + header.write(&mut cursor).unwrap_err(); + } + } + } + + proptest! { + #[test] + fn expected_payload_len( + header in macsec_any(), + ether_type in ether_type_any(), + valid_unmodified_len in 2u8..=MacsecShortLen::MAX_U8, + valid_modified_len in 1u8..=MacsecShortLen::MAX_U8 + ) { + // unmodified, payload len (non zero or one) + { + let mut header = header.clone(); + header.ptype = MacsecPType::Unmodified(ether_type); + header.short_len = MacsecShortLen::try_from_u8(valid_unmodified_len).unwrap(); + assert_eq!(Some(valid_unmodified_len as usize - 2), header.expected_payload_len()); + } + + // unmodified, unknown len + for short_len in 0..2u8 { + let mut header = header.clone(); + header.ptype = MacsecPType::Unmodified(ether_type); + header.short_len = MacsecShortLen::try_from_u8(short_len).unwrap(); + assert_eq!(None, header.expected_payload_len()); + } + + // modified, valid payload len (non zero) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut header = header.clone(); + header.ptype = ptype; + header.short_len = MacsecShortLen::try_from_u8(valid_modified_len).unwrap(); + assert_eq!(Some(valid_modified_len as usize), header.expected_payload_len()); + } + + // modified, unknown len + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut header = header.clone(); + header.ptype = ptype; + header.short_len = MacsecShortLen::ZERO; + assert_eq!(None, header.expected_payload_len()); + } + } + } + + proptest! { + #[test] + fn set_payload_len( + header in macsec_any(), + ether_type in ether_type_any(), + valid_unmodified_len in 0..=(MacsecShortLen::MAX_USIZE - 2), + invalid_unmodified_len in (MacsecShortLen::MAX_USIZE - 1)..=usize::MAX, + valid_modified_len in 1..=MacsecShortLen::MAX_USIZE, + invalid_modified_len in (MacsecShortLen::MAX_USIZE + 1)..=usize::MAX + ) { + // unmodified, payload len (non zero or one) + { + let mut header = header.clone(); + header.ptype = MacsecPType::Unmodified(ether_type); + header.set_payload_len(valid_unmodified_len); + assert_eq!(header.short_len.value() as usize, valid_unmodified_len + 2); + } + + // unmodified, invalid len + { + let mut header = header.clone(); + header.ptype = MacsecPType::Unmodified(ether_type); + header.set_payload_len(invalid_unmodified_len); + assert_eq!(0, header.short_len.value()); + } + + // modified, valid payload len (non zero) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut header = header.clone(); + header.ptype = ptype; + header.set_payload_len(valid_modified_len); + assert_eq!(valid_modified_len, header.short_len.value() as usize); + } + + // modified, unknown len + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut header = header.clone(); + header.ptype = ptype; + header.set_payload_len(invalid_modified_len); + assert_eq!(0, header.short_len.value()); + } + } + } +} diff --git a/etherparse/src/link/macsec_header_slice.rs b/etherparse/src/link/macsec_header_slice.rs new file mode 100644 index 00000000..4c0b9af3 --- /dev/null +++ b/etherparse/src/link/macsec_header_slice.rs @@ -0,0 +1,430 @@ +use crate::{ + err::{Layer, LenError}, + *, +}; + +/// Slice containing a MACsec header & next ether type (if possible). +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct MacsecHeaderSlice<'a> { + pub(crate) slice: &'a [u8], +} + +impl<'a> MacsecHeaderSlice<'a> { + /// Try creating a [`MacSecHeaderSlice`] from a slice containing the + /// MACsec header & next ether type. + pub fn from_slice( + slice: &'a [u8], + ) -> Result, err::macsec::HeaderSliceError> { + use err::macsec::{HeaderError::*, HeaderSliceError::*}; + + if slice.len() < 6 { + return Err(Len(LenError { + required_len: 6, + len: slice.len(), + len_source: LenSource::Slice, + layer: Layer::MacsecHeader, + layer_start_offset: 0, + })); + } + + // SAFETY: Safe as the length was verified to be at least 6. + let tci_an = unsafe { slice.get_unchecked(0) }; + + // validate version + if 0 != tci_an & 0b1000_0000 { + return Err(Content(UnexpectedVersion)); + } + + // validate short_len is not 1 in the unmodified case + let unmodified = 0 == tci_an & 0b1100; + if unmodified { + // SAFETY: Safe as the length was verified to be at least 6. + let short_len = unsafe { slice.get_unchecked(1) & 0b0011_1111 }; + // short len must be zero (unknown) or at least 2 in unmod + if short_len == 1 { + return Err(Content(InvalidUnmodifiedShortLen)); + } + } + + // get the encrypted, changed flag (check if ether_type can be parsed) + let required_len = + 6 + if unmodified { 2 } else { 0 } + if 0 != tci_an & 0b10_0000 { 8 } else { 0 }; + + if slice.len() < required_len { + return Err(Len(LenError { + required_len, + len: slice.len(), + len_source: LenSource::Slice, + layer: Layer::MacsecHeader, + layer_start_offset: 0, + })); + } + + Ok(MacsecHeaderSlice { + // SAFETY: Safe as the length was previously verified to be at least required_len. + slice: unsafe { core::slice::from_raw_parts(slice.as_ptr(), required_len) }, + }) + } + + /// Slice containing the header & ether type of the next segment + /// if available. + #[inline] + pub fn slice(&self) -> &'a [u8] { + self.slice + } + + /// Raw first byte of the mac sec header (containing TCI & AN). + #[inline] + pub fn tci_an_raw(&self) -> u8 { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 6. + unsafe { *self.slice.get_unchecked(0) } + } + + /// End station identifier (TCI.ES flag). + #[inline] + pub fn endstation_id(&self) -> bool { + 0 != (self.tci_an_raw() & 0b100_0000) + } + + /// Ethernet passive optical network broadcast flag. + #[inline] + pub fn tci_scb(&self) -> bool { + 0 != (self.tci_an_raw() & 0b1_0000) + } + + /// Encryption flag, which indicates whether the user data is + /// encrypted (true = encrypted, TCI.E flag). + #[inline] + pub fn encrypted(&self) -> bool { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 6. + 0 != (self.tci_an_raw() & 0b1000) + } + + /// Flag for change text, set if the user data is modified. + #[inline] + pub fn userdata_changed(&self) -> bool { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 6. + 0 != (self.tci_an_raw() & 0b100) + } + + /// True if the payload was neither flagged as modified or encrypted. + #[inline] + pub fn is_unmodified(&self) -> bool { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 6. + 0 == (self.tci_an_raw() & 0b1100) + } + + /// Payload type (contains encryption, modification flag as + /// well as the next ether type if available) + #[inline] + pub fn ptype(&self) -> MacsecPType { + let e = self.encrypted(); + let c = self.userdata_changed(); + if e { + if c { + MacsecPType::Encrypted + } else { + MacsecPType::EncryptedUnmodified + } + } else if c { + MacsecPType::Modified + } else if 0 != (self.tci_an_raw() & 0b10_0000) { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 16 + // if 0b10_0000 is set and and 'c' and 'e' are not + // set in the tci_an_raw. + MacsecPType::Unmodified(EtherType(u16::from_be_bytes(unsafe { + [*self.slice.get_unchecked(14), *self.slice.get_unchecked(15)] + }))) + } else { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 8 + // if 0b10_0000 is not set and 'c' and 'e' are not + // set in the tci_an_raw. + MacsecPType::Unmodified(EtherType(u16::from_be_bytes(unsafe { + [*self.slice.get_unchecked(6), *self.slice.get_unchecked(7)] + }))) + } + } + + /// Association number (identifies SAs). + #[inline] + pub fn an(&self) -> MacsecAn { + // SAFETY: MacSecAn conversion safe as bit-masked to only + // contain 2 bits. + unsafe { MacsecAn::new_unchecked(self.tci_an_raw() & 0b11) } + } + + /// Short length with reserved bits. + #[inline] + pub fn short_len(&self) -> MacsecShortLen { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 6. + // MacsecSl conversion safe as bit-masked to contain + // only 6 bits. + unsafe { MacsecShortLen::from_u8_unchecked(self.slice.get_unchecked(1) & 0b0011_1111) } + } + + /// Packet number. + #[inline] + pub fn packet_nr(&self) -> u32 { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 6. + // MacsecSl conversion safe as bit-masked. + u32::from_be_bytes(unsafe { + [ + *self.slice.get_unchecked(2), + *self.slice.get_unchecked(3), + *self.slice.get_unchecked(4), + *self.slice.get_unchecked(5), + ] + }) + } + + /// True if the SCI bit is set in the TCI part of the SecTag header. + #[inline] + pub fn sci_present(&self) -> bool { + 0 != (self.tci_an_raw() & 0b10_0000) + } + + /// Secure channel identifier. + #[inline] + pub fn sci(&self) -> Option { + if self.sci_present() { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 14 + // if 0b10_0000 is set in the tci_an_raw. + Some(u64::from_be_bytes(unsafe { + [ + *self.slice.get_unchecked(6), + *self.slice.get_unchecked(7), + *self.slice.get_unchecked(8), + *self.slice.get_unchecked(9), + *self.slice.get_unchecked(10), + *self.slice.get_unchecked(11), + *self.slice.get_unchecked(12), + *self.slice.get_unchecked(13), + ] + })) + } else { + None + } + } + + /// Ether type of the data following the sec tag (only + /// available if not encrypted and userdata is not flagged + /// as modified). + #[inline] + pub fn next_ether_type(&self) -> Option { + if 0 != self.tci_an_raw() & 0b1100 { + None + } else if self.sci_present() { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 16 + // if 0b10_0000 is set and 0b1100 is not set in + // the tci_an_raw. + Some(EtherType(u16::from_be_bytes(unsafe { + [*self.slice.get_unchecked(14), *self.slice.get_unchecked(15)] + }))) + } else { + // SAFETY: Slice access safe as length of the slice was + // verified in the constructor to be at least 8 + // if 0b10_0000 is not set and 0b1100 is not set in + // the tci_an_raw. + Some(EtherType(u16::from_be_bytes(unsafe { + [*self.slice.get_unchecked(6), *self.slice.get_unchecked(7)] + }))) + } + } + + /// Length of the MACsec header (SecTag + next ether type if available). + #[inline] + pub fn header_len(&self) -> usize { + 6 + if self.sci_present() { 8 } else { 0 } + if self.is_unmodified() { 2 } else { 0 } + } + + /// Returns the required length of the payload (data after header + + /// next_ether_type if present) if possible. + /// + /// If the length cannot be determined (`short_len` is zero or less then + /// `2` when `ptype` `Unmodified`) `None` is returned. + #[inline] + pub fn expected_payload_len(&self) -> Option { + let sl = self.short_len().value() as usize; + if sl > 0 { + if 0 != self.tci_an_raw() & 0b1100 { + // no ether type (encrypted and/or modified payload) + Some(sl) + } else if sl < 2 { + None + } else { + Some(sl - 2) + } + } else { + None + } + } + + /// Decodes all MacSecHeader values and returns them as a + /// [`crate::MacSecHeader`]. + #[inline] + pub fn to_header(&self) -> MacsecHeader { + MacsecHeader { + ptype: self.ptype(), + endstation_id: self.endstation_id(), + scb: self.tci_scb(), + an: self.an(), + short_len: self.short_len(), + packet_nr: self.packet_nr(), + sci: self.sci(), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_gens::*; + use arrayvec::ArrayVec; + use proptest::prelude::*; + + proptest! { + #[test] + fn from_slice( + macsec in macsec_any(), + ether_type in ether_type_any(), + sci in any::() + ) { + use MacsecPType::*; + use err::macsec::*; + + // variants + for ptype in [Unmodified(ether_type), Modified, Encrypted, EncryptedUnmodified] { + for has_sci in [false, true] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.sci = if has_sci { + Some(sci) + } else { + None + }; + if matches!(ptype, MacsecPType::Unmodified(_)) && macsec.short_len.value() == 1 { + macsec.short_len = MacsecShortLen::ZERO; + } + + // ok case + { + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&[1]).unwrap(); + let m = MacsecHeaderSlice::from_slice(&bytes).unwrap(); + assert_eq!(m.to_header(), macsec); + assert_eq!(m.slice(), &bytes[..bytes.len() - 1]); + } + + // version error + { + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&[1]).unwrap(); + + // version bit + bytes[0] = bytes[0] | 0b1000_0000; + + let m = MacsecHeaderSlice::from_slice(&bytes); + assert_eq!(m, Err(HeaderSliceError::Content(HeaderError::UnexpectedVersion))); + } + + // short len error + if matches!(ptype, MacsecPType::Unmodified(_)) { + let mut macsec = macsec.clone(); + macsec.short_len = MacsecShortLen::try_from_u8(1).unwrap(); + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&[1]).unwrap(); + + let m = MacsecHeaderSlice::from_slice(&bytes); + assert_eq!(m, Err(HeaderSliceError::Content(HeaderError::InvalidUnmodifiedShortLen))); + } + + // len error + for len in 0..macsec.header_len() { + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&[1]).unwrap(); + + let m = MacsecHeaderSlice::from_slice(&bytes[..len]); + assert_eq!( + m, + Err(HeaderSliceError::Len(err::LenError{ + required_len: if len < 6 { + 6 + } else { + macsec.header_len() + }, + len, + len_source: LenSource::Slice, + layer: Layer::MacsecHeader, + layer_start_offset: 0, + })) + ); + } + } + } + } + } + + proptest! { + #[test] + fn expected_payload_len( + header in macsec_any(), + ether_type in ether_type_any(), + valid_unmodified_len in 2u8..=MacsecShortLen::MAX_U8, + valid_modified_len in 1u8..=MacsecShortLen::MAX_U8 + ) { + // unmodified, payload len (non zero or one) + { + let mut header = header.clone(); + header.ptype = MacsecPType::Unmodified(ether_type); + header.short_len = MacsecShortLen::try_from_u8(valid_unmodified_len).unwrap(); + let bytes = header.to_bytes(); + let slice = MacsecHeaderSlice::from_slice(&bytes).unwrap(); + assert_eq!(Some(valid_unmodified_len as usize - 2), slice.expected_payload_len()); + } + + // unmodified, unknown len + for short_len in 0..2u8 { + let mut header = header.clone(); + header.ptype = MacsecPType::Unmodified(ether_type); + header.short_len = MacsecShortLen::try_from_u8(short_len).unwrap(); + let bytes = header.to_bytes(); + let slice = MacsecHeaderSlice{ slice: &bytes }; + assert_eq!(None, slice.expected_payload_len()); + } + + // modified, valid payload len (non zero) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut header = header.clone(); + header.ptype = ptype; + header.short_len = MacsecShortLen::try_from_u8(valid_modified_len).unwrap(); + let bytes = header.to_bytes(); + let slice = MacsecHeaderSlice::from_slice(&bytes).unwrap(); + assert_eq!(Some(valid_modified_len as usize), slice.expected_payload_len()); + } + + // modified, unknown len + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut header = header.clone(); + header.ptype = ptype; + header.short_len = MacsecShortLen::ZERO; + let bytes = header.to_bytes(); + let slice = MacsecHeaderSlice::from_slice(&bytes).unwrap(); + assert_eq!(None, slice.expected_payload_len()); + } + } + } +} diff --git a/etherparse/src/link/macsec_payload_slice.rs b/etherparse/src/link/macsec_payload_slice.rs new file mode 100644 index 00000000..b8e9d7be --- /dev/null +++ b/etherparse/src/link/macsec_payload_slice.rs @@ -0,0 +1,10 @@ +use super::EtherPayloadSlice; + +#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub enum MacsecPayloadSlice<'a> { + /// Unencrypted unmodified ether payload. + Unmodified(EtherPayloadSlice<'a>), + + /// Modified payload (either by encryption or other algorithm). + Modified(&'a [u8]), +} diff --git a/etherparse/src/link/macsec_ptype.rs b/etherparse/src/link/macsec_ptype.rs new file mode 100644 index 00000000..dd99ed9c --- /dev/null +++ b/etherparse/src/link/macsec_ptype.rs @@ -0,0 +1,24 @@ +use super::EtherType; + +/// Encryption & modification state of the payload +/// of a mac sec packet including the next ether type if +/// the payload is unencrypted & unmodified. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum MacsecPType { + /// Unencrypted & unmodified (`!tci.c && !tci.e`) containing + /// the ether type of the after the mac sec. + Unmodified(EtherType), + + /// Unencrypted but modified payload (`tci.c && !tci.e`). + Modified, + + /// Encrypted and modified payload (`tci.c && tci.e`). + Encrypted, + + /// Encrypted and unmodified payload (`tci.c && !tci.e`). + /// This is not normal behavior. + /// + /// Normally if the "encryption" flag should always be set + /// together with the modification flag. + EncryptedUnmodified, +} diff --git a/etherparse/src/link/macsec_short_len.rs b/etherparse/src/link/macsec_short_len.rs new file mode 100644 index 00000000..ea549454 --- /dev/null +++ b/etherparse/src/link/macsec_short_len.rs @@ -0,0 +1,298 @@ +use crate::err::ValueTooBigError; + +/// 6 bit unsigned integer containing the "MACsec short length". +/// (present in the [`crate::MacSecHeader`]). +#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct MacsecShortLen(u8); + +impl MacsecShortLen { + /// MacsecShortLen with value 0. + pub const ZERO: MacsecShortLen = MacsecShortLen(0); + + /// Maximum value of a "MACsec short length" as a [`u8`]. + pub const MAX_U8: u8 = 0b0011_1111; + + /// Maximum value of a "MACsec short length" as a [`usize`]. + pub const MAX_USIZE: usize = 0b0011_1111; + + /// Tries to create an [`MacsecShortLen`] and checks that the passed value + /// is smaller or equal than [`MacsecShortLen::MAX_U8`] (6 bit unsigned integer). + /// + /// In case the passed value is bigger then what can be represented in an 6 bit + /// integer an error is returned. Otherwise an `Ok` containing the [`MacsecShortLen`]. + /// + /// ``` + /// use etherparse::MacsecShortLen; + /// + /// let an = MacsecShortLen::try_from_u8(2).unwrap(); + /// assert_eq!(an.value(), 2); + /// + /// // if a number that can not be represented in an 2 bit integer + /// // gets passed in an error is returned + /// use etherparse::err::{ValueTooBigError, ValueType}; + /// assert_eq!( + /// MacsecShortLen::try_from_u8(MacsecShortLen::MAX_U8 + 1), + /// Err(ValueTooBigError{ + /// actual: MacsecShortLen::MAX_U8 + 1, + /// max_allowed: MacsecShortLen::MAX_U8, + /// value_type: ValueType::MacsecShortLen, + /// }) + /// ); + /// ``` + #[inline] + pub const fn try_from_u8(value: u8) -> Result> { + use crate::err::ValueType; + if value <= MacsecShortLen::MAX_U8 { + Ok(MacsecShortLen(value)) + } else { + Err(ValueTooBigError { + actual: value, + max_allowed: MacsecShortLen::MAX_U8, + value_type: ValueType::MacsecShortLen, + }) + } + } + + /// Creates an [`MacsecShortLen`] without checking that the value + /// is smaller or equal than [`MacsecShortLen::MAX_U8`] (6 bit unsigned integer). + /// The caller must guarantee that `value <= MacsecShortLen::MAX_U8`. + /// + /// # Safety + /// + /// `value` must be smaller or equal than [`MacsecShortLen::MAX_U8`] + /// otherwise the behavior of functions or data structures relying + /// on this pre-requirement is undefined. + #[inline] + pub const unsafe fn from_u8_unchecked(value: u8) -> MacsecShortLen { + debug_assert!(value <= MacsecShortLen::MAX_U8); + MacsecShortLen(value) + } + + /// Creates an [`MacsecShortLen`] from a length and automatically + /// defaults to zero if too big. This mirrors the expected behavior + /// of the `short_len` field in the [`MacsecHeader`]. + /// + /// # Example + /// ``` + /// use etherparse::MacsecShortLen; + /// + /// // if the length is smaller than 64. + /// let a = MacsecShortLen::from_len(34); + /// assert_eq!(34, a.value()); + /// + /// // if the length is greater than 64 [`MacsecShortLen::MAX_U8`] + /// // zero is returned + /// let b = MacsecShortLen::from_len(65); + /// assert_eq!(0, b.value()); + /// ``` + #[inline] + pub fn from_len(len: usize) -> MacsecShortLen { + if len > 0b0011_1111 { + MacsecShortLen::ZERO + } else { + MacsecShortLen(len as u8) + } + } + + /// Returns the underlying unsigned 6 bit value as an `u8` value. + #[inline] + pub const fn value(self) -> u8 { + self.0 + } +} + +impl core::fmt::Display for MacsecShortLen { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } +} + +impl From for u8 { + #[inline] + fn from(value: MacsecShortLen) -> Self { + value.0 + } +} + +impl TryFrom for MacsecShortLen { + type Error = ValueTooBigError; + + #[inline] + fn try_from(value: u8) -> Result { + use crate::err::ValueType; + if value <= MacsecShortLen::MAX_U8 { + Ok(MacsecShortLen(value)) + } else { + Err(Self::Error { + actual: value, + max_allowed: MacsecShortLen::MAX_U8, + value_type: ValueType::MacsecShortLen, + }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use core::hash::{Hash, Hasher}; + use proptest::prelude::*; + use std::format; + + #[test] + fn derived_traits() { + // copy & clone + { + let a = MacsecShortLen(2); + let b = a; + assert_eq!(a, b); + assert_eq!(a.clone(), a); + } + + // default + { + let actual: MacsecShortLen = Default::default(); + assert_eq!(actual.value(), 0); + } + + // debug + { + let a = MacsecShortLen(2); + assert_eq!(format!("{:?}", a), format!("MacsecShortLen(2)")); + } + + // ord & partial ord + { + use core::cmp::Ordering; + let a = MacsecShortLen(2); + let b = a; + assert_eq!(a.cmp(&b), Ordering::Equal); + assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal)); + } + + // hash + { + use std::collections::hash_map::DefaultHasher; + let a = { + let mut hasher = DefaultHasher::new(); + MacsecShortLen(2).hash(&mut hasher); + hasher.finish() + }; + let b = { + let mut hasher = DefaultHasher::new(); + MacsecShortLen(2).hash(&mut hasher); + hasher.finish() + }; + assert_eq!(a, b); + } + } + + proptest! { + #[test] + fn try_from_u8( + valid_value in 0..=0b0011_1111u8, + invalid_value in 0b0100_0000u8..=u8::MAX + ) { + use crate::err::{ValueType, ValueTooBigError}; + assert_eq!( + valid_value, + MacsecShortLen::try_from_u8(valid_value).unwrap().value() + ); + assert_eq!( + MacsecShortLen::try_from_u8(invalid_value).unwrap_err(), + ValueTooBigError{ + actual: invalid_value, + max_allowed: 0b0011_1111, + value_type: ValueType::MacsecShortLen + } + ); + } + } + + proptest! { + #[test] + fn try_from( + valid_value in 0..=0b0011_1111u8, + invalid_value in 0b0100_0000u8..=u8::MAX + ) { + use crate::err::{ValueType, ValueTooBigError}; + // try_into + { + let actual: MacsecShortLen = valid_value.try_into().unwrap(); + assert_eq!(actual.value(), valid_value); + + let err: Result> = invalid_value.try_into(); + assert_eq!( + err.unwrap_err(), + ValueTooBigError{ + actual: invalid_value, + max_allowed: 0b0011_1111, + value_type: ValueType::MacsecShortLen + } + ); + } + // try_from + { + assert_eq!( + MacsecShortLen::try_from(valid_value).unwrap().value(), + valid_value + ); + + assert_eq!( + MacsecShortLen::try_from(invalid_value).unwrap_err(), + ValueTooBigError{ + actual: invalid_value, + max_allowed: 0b0011_1111, + value_type: ValueType::MacsecShortLen + } + ); + } + } + } + + proptest! { + #[test] + fn from_u8_unchecked(valid_value in 0..=0b0011_1111u8) { + assert_eq!( + valid_value, + unsafe { + MacsecShortLen::from_u8_unchecked(valid_value).value() + } + ); + } + } + + proptest! { + #[test] + fn from_len( + valid_value in 0..=0b0011_1111usize, + zero_values in 0b0100_0000usize..=usize::MAX, + ) { + assert_eq!( + valid_value as u8, + MacsecShortLen::from_len(valid_value).value() + ); + assert_eq!( + 0, + MacsecShortLen::from_len(zero_values).value() + ); + } + } + + proptest! { + #[test] + fn fmt(valid_value in 0..=0b0011_1111u8) { + assert_eq!(format!("{}", MacsecShortLen(valid_value)), format!("{}", valid_value)); + } + } + + proptest! { + #[test] + fn from(valid_value in 0..=0b0011_1111u8,) { + let pcp = MacsecShortLen::try_from_u8(valid_value).unwrap(); + let actual: u8 = pcp.into(); + assert_eq!(actual, valid_value); + } + } +} diff --git a/etherparse/src/link/macsec_slice.rs b/etherparse/src/link/macsec_slice.rs new file mode 100644 index 00000000..9a419c5b --- /dev/null +++ b/etherparse/src/link/macsec_slice.rs @@ -0,0 +1,292 @@ +use crate::{ + err::{macsec, Layer, LenError}, + *, +}; + +/// MACsec packet (SecTag header & payload). +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MacsecSlice<'a> { + pub header: MacsecHeaderSlice<'a>, + pub payload: MacsecPayloadSlice<'a>, +} + +impl<'a> MacsecSlice<'a> { + pub fn from_slice(slice: &'a [u8]) -> Result, macsec::HeaderSliceError> { + use macsec::HeaderSliceError::Len; + + let header = MacsecHeaderSlice::from_slice(slice)?; + + // validate the length of the slice if the short length is set + let (payload_slice, len_source) = + if let Some(req_payload_len) = header.expected_payload_len() { + let required_len = header.slice().len() + req_payload_len; + if slice.len() < required_len { + return Err(Len(LenError { + required_len, + len: slice.len(), + len_source: LenSource::MacsecShortLength, + layer: Layer::MacsecPacket, + layer_start_offset: 0, + })); + } + // SAFETY: Safe as the length was verified above to be at least required_len. + ( + unsafe { + core::slice::from_raw_parts( + slice.as_ptr().add(header.slice().len()), + req_payload_len, + ) + }, + LenSource::MacsecShortLength, + ) + } else { + // SAFETY: Safe as the header is a subslice of the original slice. + ( + unsafe { + core::slice::from_raw_parts( + slice.as_ptr().add(header.slice().len()), + slice.len() - header.slice().len(), + ) + }, + LenSource::Slice, + ) + }; + + let payload = if let Some(ether_type) = header.next_ether_type() { + MacsecPayloadSlice::Unmodified(EtherPayloadSlice { + ether_type, + len_source, + payload: payload_slice, + }) + } else { + MacsecPayloadSlice::Modified(payload_slice) + }; + + Ok(MacsecSlice { header, payload }) + } + + /// Get the ether payload if the macsec packet is unencrypted & unmodified. + pub fn ether_payload(&self) -> Option> { + if let MacsecPayloadSlice::Unmodified(e) = &self.payload { + Some(e.clone()) + } else { + None + } + } + + /// Get the ether type of the payload if the macsec packet is unencrypted & unmodified. + pub fn next_ether_type(&self) -> Option { + self.header.next_ether_type() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{err::LenError, test_gens::*}; + use arrayvec::ArrayVec; + use proptest::prelude::*; + + proptest! { + #[test] + fn from_slice( + macsec in macsec_any(), + ethertype in ether_type_any(), + non_zero_sl_unmodified in 3u8..=0b0011_1111, + non_zero_sl_modified in 1u8..=0b0011_1111 + ) { + // macsec (unmodified, complete, nonzero short length) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_unmodified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..(non_zero_sl_unmodified - 2) { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = MacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + MacsecPayloadSlice::Unmodified(EtherPayloadSlice{ + ether_type: ethertype, + len_source: LenSource::MacsecShortLength, + payload: &payload + }) + ); + assert_eq!( + m.ether_payload(), + Some(EtherPayloadSlice{ + ether_type: ethertype, + len_source: LenSource::MacsecShortLength, + payload: &payload + }) + ); + assert_eq!(m.next_ether_type(), Some(ethertype)); + } + // macsec (incomplete, nonzero short length) + for ptype in [MacsecPType::Unmodified(ethertype), MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + let mut payload = ArrayVec::::new(); + if matches!(ptype, MacsecPType::Unmodified(_)) { + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_unmodified).unwrap(); + for v in 0..non_zero_sl_unmodified-3 { + payload.push(v); + } + } else { + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_modified).unwrap(); + for v in 0..non_zero_sl_modified-1 { + payload.push(v); + } + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = MacsecSlice::from_slice(&bytes); + assert_eq!( + m, + Err(macsec::HeaderSliceError::Len(LenError{ + required_len: if matches!(ptype, MacsecPType::Unmodified(_)) { + macsec.header_len() + non_zero_sl_unmodified as usize - 2 + } else { + macsec.header_len() + non_zero_sl_modified as usize + }, + len: bytes.len(), + len_source: LenSource::MacsecShortLength, + layer: err::Layer::MacsecPacket, + layer_start_offset: 0 + })) + ); + } + // macsec (unmodified, zero short length) + { + let mut macsec = macsec.clone(); + macsec.ptype = MacsecPType::Unmodified(ethertype); + macsec.short_len = MacsecShortLen::ZERO; + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_unmodified+1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = MacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + MacsecPayloadSlice::Unmodified(EtherPayloadSlice{ + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + assert_eq!( + m.ether_payload(), + Some(EtherPayloadSlice{ + ether_type: ethertype, + len_source: LenSource::Slice, + payload: &payload + }) + ); + assert_eq!(m.next_ether_type(), Some(ethertype)); + } + // macsec (modified, complete, nonzero short length) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_modified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_modified { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = MacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + MacsecPayloadSlice::Modified(&payload), + ); + assert_eq!(m.ether_payload(), None); + assert_eq!(m.next_ether_type(), None); + } + // macsec (modified, incomplete, nonzero short length) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::try_from(non_zero_sl_modified).unwrap(); + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_modified-1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = MacsecSlice::from_slice(&bytes); + assert_eq!( + m, + Err(macsec::HeaderSliceError::Len(LenError{ + required_len: macsec.header_len() + non_zero_sl_modified as usize, + len: bytes.len(), + len_source: LenSource::MacsecShortLength, + layer: err::Layer::MacsecPacket, + layer_start_offset: 0 + })) + ); + } + // macsec (modified, zero short length) + for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::ZERO; + + let mut payload = ArrayVec::::new(); + for v in 0..non_zero_sl_modified+1 { + payload.push(v); + } + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + bytes.try_extend_from_slice(&payload).unwrap(); + let m = MacsecSlice::from_slice(&bytes).unwrap(); + assert_eq!( + m.payload, + MacsecPayloadSlice::Modified(&payload) + ); + assert_eq!(m.ether_payload(), None); + assert_eq!(m.next_ether_type(), None); + } + // header parse error + for ptype in [MacsecPType::Unmodified(ethertype), MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] { + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.short_len = MacsecShortLen::ZERO; + + let mut bytes = ArrayVec::::new(); + bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap(); + let m = MacsecSlice::from_slice(&bytes[..bytes.len() - 1]); + assert_eq!( + m, + Err(macsec::HeaderSliceError::Len(LenError{ + required_len: macsec.header_len(), + len: macsec.header_len() - 1, + len_source: LenSource::Slice, + layer: err::Layer::MacsecHeader, + layer_start_offset: 0 + })) + ); + } + } + } +} diff --git a/etherparse/src/link/mod.rs b/etherparse/src/link/mod.rs index 5f94511e..4ba361fa 100644 --- a/etherparse/src/link/mod.rs +++ b/etherparse/src/link/mod.rs @@ -1,9 +1,6 @@ mod double_vlan_header; pub use double_vlan_header::*; -mod double_vlan_header_slice; -pub use double_vlan_header_slice::*; - mod double_vlan_slice; pub use double_vlan_slice::*; @@ -22,6 +19,24 @@ pub use ethernet2_header_slice::*; mod ethernet2_slice; pub use ethernet2_slice::*; +mod lax_ether_payload_slice; +pub use lax_ether_payload_slice::*; + +mod lax_link_ext_slice; +pub use lax_link_ext_slice::*; + +mod lax_macsec_payload_slice; +pub use lax_macsec_payload_slice::*; + +mod lax_macsec_slice; +pub use lax_macsec_slice::*; + +mod link_ext_header; +pub use link_ext_header::*; + +mod link_ext_slice; +pub use link_ext_slice::*; + mod link_header; pub use link_header::*; @@ -49,6 +64,27 @@ pub use linux_sll_protocol_type::*; mod linux_sll_slice; pub use linux_sll_slice::*; +mod macsec_an; +pub use macsec_an::*; + +mod macsec_header_slice; +pub use macsec_header_slice::*; + +mod macsec_header; +pub use macsec_header::*; + +mod macsec_payload_slice; +pub use macsec_payload_slice::*; + +mod macsec_short_len; +pub use macsec_short_len::*; + +mod macsec_slice; +pub use macsec_slice::*; + +mod macsec_ptype; +pub use macsec_ptype::*; + mod single_vlan_header; pub use single_vlan_header::*; diff --git a/etherparse/src/link/single_vlan_slice.rs b/etherparse/src/link/single_vlan_slice.rs index 77a060ea..a8c4f50b 100644 --- a/etherparse/src/link/single_vlan_slice.rs +++ b/etherparse/src/link/single_vlan_slice.rs @@ -38,7 +38,7 @@ impl<'a> SingleVlanSlice<'a> { pub fn priority_code_point(&self) -> VlanPcp { unsafe { // SAFETY: Safe as slice len checked in constructor to be at least 4 & - // the bitmask guarantees values does not exceed 0b0000_0111. + // the bit mask guarantees values does not exceed 0b0000_0111. VlanPcp::new_unchecked((*self.slice.get_unchecked(0) >> 5) & 0b0000_0111) } } @@ -54,14 +54,14 @@ impl<'a> SingleVlanSlice<'a> { unsafe { 0 != (*self.slice.get_unchecked(0) & 0x10) } } - /// Reads the 12 bits "vland identifier" field from the VLAN header. + /// Reads the 12 bits "vlan identifier" field from the VLAN header. #[inline] pub fn vlan_identifier(&self) -> VlanId { // SAFETY: // Slice len checked in constructor to be at least 4 & - // value and the value is guranteed not to exceed + // value and the value is guaranteed not to exceed // 0b0000_1111_1111_1111 as the upper bits have been - // bitmasked out. + // bit-masked out. unsafe { VlanId::new_unchecked(u16::from_be_bytes([ *self.slice.get_unchecked(0) & 0b0000_1111, @@ -95,7 +95,7 @@ impl<'a> SingleVlanSlice<'a> { pub fn header_slice(&self) -> &[u8] { unsafe { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of SingleVlanHeader::LEN (4). core::slice::from_raw_parts(self.slice.as_ptr(), SingleVlanHeader::LEN) } @@ -107,6 +107,7 @@ impl<'a> SingleVlanSlice<'a> { pub fn payload(&self) -> EtherPayloadSlice<'a> { EtherPayloadSlice { ether_type: self.ether_type(), + len_source: LenSource::Slice, payload: self.payload_slice(), } } @@ -116,7 +117,7 @@ impl<'a> SingleVlanSlice<'a> { pub fn payload_slice(&self) -> &'a [u8] { unsafe { // SAFETY: - // Safe as the contructor checks that the slice has + // Safe as the constructor checks that the slice has // at least the length of SingleVlanHeader::LEN (4). core::slice::from_raw_parts( self.slice.as_ptr().add(SingleVlanHeader::LEN), @@ -201,6 +202,7 @@ mod test { assert_eq!( EtherPayloadSlice { ether_type: vlan.ether_type, + len_source: LenSource::Slice, payload: &data[SingleVlanHeader::LEN..], }, slice.payload() diff --git a/etherparse/src/link/vlan_header.rs b/etherparse/src/link/vlan_header.rs index f01a1974..58def16a 100644 --- a/etherparse/src/link/vlan_header.rs +++ b/etherparse/src/link/vlan_header.rs @@ -17,18 +17,6 @@ impl VlanHeader { ether_type::VLAN_DOUBLE_TAGGED_FRAME, ]; - /// Write the IEEE 802.1Q VLAN single or double tagging header - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - #[inline] - pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { - use VlanHeader::*; - match &self { - Single(header) => header.write(writer), - Double(header) => header.write(writer), - } - } - /// Returns the ether type of the next header after the vlan header(s). #[inline] pub fn next_header(&self) -> EtherType { @@ -37,23 +25,13 @@ impl VlanHeader { VlanHeader::Double(d) => d.inner.ether_type, } } - - /// Length of the serialized header(s) in bytes. - #[inline] - pub fn header_len(&self) -> usize { - use VlanHeader::*; - match &self { - Single(_) => SingleVlanHeader::LEN, - Double(_) => DoubleVlanHeader::LEN, - } - } } #[cfg(test)] mod test { use super::*; use crate::test_gens::*; - use alloc::{format, vec::Vec}; + use alloc::format; use proptest::prelude::*; #[test] @@ -135,61 +113,4 @@ mod test { } } } - - proptest! { - #[test] - fn header_len( - single in vlan_single_any(), - double in vlan_double_any(), - ) { - // single - assert_eq!( - SingleVlanHeader::LEN, - VlanHeader::Single(single.clone()).header_len() - ); - // double - assert_eq!( - DoubleVlanHeader::LEN, - VlanHeader::Double(double.clone()).header_len() - ); - } - } - - proptest! { - #[test] - fn write( - single in vlan_single_any(), - double in vlan_double_any(), - ) { - // single - { - let expected = { - let mut buffer = Vec::with_capacity(single.header_len()); - single.write(&mut buffer).unwrap(); - buffer - }; - let actual = { - let mut buffer = Vec::with_capacity(single.header_len()); - VlanHeader::Single(single.clone()).write(&mut buffer).unwrap(); - buffer - }; - assert_eq!(expected, actual); - } - - // double - { - let expected = { - let mut buffer = Vec::with_capacity(double.header_len()); - double.write(&mut buffer).unwrap(); - buffer - }; - let actual = { - let mut buffer = Vec::with_capacity(double.header_len()); - VlanHeader::Double(double.clone()).write(&mut buffer).unwrap(); - buffer - }; - assert_eq!(expected, actual); - } - } - } } diff --git a/etherparse/src/link/vlan_slice.rs b/etherparse/src/link/vlan_slice.rs index 83890852..1e1eac32 100644 --- a/etherparse/src/link/vlan_slice.rs +++ b/etherparse/src/link/vlan_slice.rs @@ -31,7 +31,7 @@ impl<'a> VlanSlice<'a> { #[cfg(test)] mod test { use crate::{test_gens::*, *}; - use alloc::format; + use alloc::{format, vec::Vec}; use proptest::prelude::*; proptest! { @@ -54,9 +54,17 @@ mod test { // double { - let raw = double.to_bytes(); + let mut raw = Vec::with_capacity( + SingleVlanHeader::LEN*2 + ); + raw.extend_from_slice(&double.outer.to_bytes()); + raw.extend_from_slice(&double.inner.to_bytes()); + let slice = VlanSlice::DoubleVlan( - DoubleVlanSlice::from_slice(&raw).unwrap() + DoubleVlanSlice{ + outer: SingleVlanSlice::from_slice(&raw[..]).unwrap(), + inner: SingleVlanSlice::from_slice(&raw[SingleVlanHeader::LEN..]).unwrap(), + } ); assert_eq!( slice.to_header(), @@ -66,6 +74,54 @@ mod test { } } + proptest! { + #[test] + fn payload( + outer in vlan_single_any(), + inner in vlan_single_any() + ) { + let payload = [1,2,3,4]; + + // single + { + let mut bytes = Vec::with_capacity(SingleVlanHeader::LEN + 4); + bytes.extend_from_slice(&outer.to_bytes()); + bytes.extend_from_slice(&payload); + let slice = VlanSlice::SingleVlan( + SingleVlanSlice::from_slice(&bytes).unwrap() + ); + assert_eq!( + slice.payload(), + EtherPayloadSlice{ + ether_type: outer.ether_type, + len_source: LenSource::Slice, + payload: &payload, + } + ); + } + + // double + { + let mut bytes = Vec::with_capacity(SingleVlanHeader::LEN + 4); + bytes.extend_from_slice(&outer.to_bytes()); + bytes.extend_from_slice(&inner.to_bytes()); + bytes.extend_from_slice(&payload); + let slice = VlanSlice::DoubleVlan(DoubleVlanSlice{ + outer: SingleVlanSlice::from_slice(&bytes).unwrap(), + inner: SingleVlanSlice::from_slice(&bytes[SingleVlanHeader::LEN..]).unwrap() + }); + assert_eq!( + slice.payload(), + EtherPayloadSlice{ + ether_type: inner.ether_type, + len_source: LenSource::Slice, + payload: &payload, + } + ); + } + } + } + proptest! { #[test] fn debug( @@ -84,11 +140,20 @@ mod test { // double { - let raw = double.to_bytes(); - let d = DoubleVlanSlice::from_slice(&raw).unwrap(); + let mut raw = Vec::with_capacity( + SingleVlanHeader::LEN*2 + ); + raw.extend_from_slice(&double.outer.to_bytes()); + raw.extend_from_slice(&double.inner.to_bytes()); + + let inner = DoubleVlanSlice{ + outer: SingleVlanSlice::from_slice(&raw[..]).unwrap(), + inner: SingleVlanSlice::from_slice(&raw[SingleVlanHeader::LEN..]).unwrap(), + }; + let d = VlanSlice::DoubleVlan(inner.clone()); assert_eq!( - format!("{:?}", VlanSlice::DoubleVlan(d.clone())), - format!("DoubleVlan({:?})", d) + format!("{:?}", d.clone()), + format!("DoubleVlan({:?})", inner) ); } } @@ -111,9 +176,17 @@ mod test { // double { - let raw = double.to_bytes(); + let mut raw = Vec::with_capacity( + SingleVlanHeader::LEN*2 + ); + raw.extend_from_slice(&double.outer.to_bytes()); + raw.extend_from_slice(&double.inner.to_bytes()); + let d = VlanSlice::DoubleVlan( - DoubleVlanSlice::from_slice(&raw).unwrap() + DoubleVlanSlice{ + outer: SingleVlanSlice::from_slice(&raw[..]).unwrap(), + inner: SingleVlanSlice::from_slice(&raw[SingleVlanHeader::LEN..]).unwrap(), + } ); assert_eq!(d.clone(), d); } diff --git a/etherparse/src/net/arp_packet.rs b/etherparse/src/net/arp_packet.rs index efbc0be0..f24db2f8 100644 --- a/etherparse/src/net/arp_packet.rs +++ b/etherparse/src/net/arp_packet.rs @@ -78,16 +78,16 @@ impl ArpPacket { Ok(ArpPacket { hw_addr_type, proto_addr_type, - // cast ok as we verfied the len to be less equal then 255. + // cast ok as we verified the len to be less equal then 255. hw_addr_size: sender_hw_addr.len() as u8, - // cast ok as we verfied the len to be less equal then 255. + // cast ok as we verified the len to be less equal then 255. proto_addr_size: sender_protocol_addr.len() as u8, operation, sender_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * sender_hw_addr.len() is guranteed to be <= 255 (checked in if above) - // * memory areas guranteed to be non overlapping (buf created in this function). + // * sender_hw_addr.len() is guaranteed to be <= 255 (checked in if above) + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_hw_addr.as_ptr(), @@ -100,8 +100,8 @@ impl ArpPacket { sender_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * sender_protocol_addr.len() is guranteed to be <= 255 (checked in if above) - // * memory areas guranteed to be non overlapping (buf created in this function). + // * sender_protocol_addr.len() is guaranteed to be <= 255 (checked in if above) + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_protocol_addr.as_ptr(), @@ -114,8 +114,8 @@ impl ArpPacket { target_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * target_hw_addr.len() is guranteed to be <= 255 (checked in if above) - // * memory areas guranteed to be non overlapping (buf created in this function). + // * target_hw_addr.len() is guaranteed to be <= 255 (checked in if above) + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_hw_addr.as_ptr(), @@ -128,8 +128,8 @@ impl ArpPacket { target_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * target_protocol_addr.len() is guranteed to be <= 255 (checked in if above) - // * memory areas guranteed to be non overlapping (buf created in this function). + // * target_protocol_addr.len() is guaranteed to be <= 255 (checked in if above) + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_protocol_addr.as_ptr(), @@ -147,12 +147,12 @@ impl ArpPacket { /// /// # Safety /// - /// The caller must gurantee that + /// The caller must guarantee that /// /// * `sender_hw_addr` & `target_hw_addr` have the same length and the length must be smaller or equal than 255. /// * `sender_protocol_addr` & `target_protocol_addr` have the same length and the length must be smaller or equal than 255. /// - /// The gurantees the caller must fullfill are equal to the following + /// The guarantees the caller must fulfill are equal to the following /// preconditions: /// /// * `sender_hw_addr.len() == target_hw_addr.len()` @@ -178,16 +178,16 @@ impl ArpPacket { ArpPacket { hw_addr_type, proto_addr_type, - // cast ok as we verfied the len to be less equal then 255. + // cast ok as we verified the len to be less equal then 255. hw_addr_size: sender_hw_addr.len() as u8, - // cast ok as we verfied the len to be less equal then 255. + // cast ok as we verified the len to be less equal then 255. proto_addr_size: sender_protocol_addr.len() as u8, operation, sender_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * the caller must gurantee that sender_hw_addr.len() is <= 255 - // * memory areas guranteed to be non overlapping (buf created in this function). + // * the caller must guarantee that sender_hw_addr.len() is <= 255 + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_hw_addr.as_ptr(), @@ -200,8 +200,8 @@ impl ArpPacket { sender_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * the caller must gurantee that sender_protocol_addr.len() is <= 255 - // * memory areas guranteed to be non overlapping (buf created in this function). + // * the caller must guarantee that sender_protocol_addr.len() is <= 255 + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_protocol_addr.as_ptr(), @@ -214,8 +214,8 @@ impl ArpPacket { target_hw_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * the caller must gurantee that target_hw_addr.len() is <= 255 - // * memory areas guranteed to be non overlapping (buf created in this function). + // * the caller must guarantee that target_hw_addr.len() is <= 255 + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_hw_addr.as_ptr(), @@ -228,8 +228,8 @@ impl ArpPacket { target_protocol_addr_buf: { let mut buf: [MaybeUninit; 255] = [const { MaybeUninit::uninit() }; 255]; // SAFETY: Safe as - // * the caller must gurantee that target_protocol_addr.len() is <= 255 - // * memory areas guranteed to be non overlapping (buf created in this function). + // * the caller must guarantee that target_protocol_addr.len() is <= 255 + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_protocol_addr.as_ptr(), @@ -321,8 +321,8 @@ impl ArpPacket { } { // SAFETY: Safe as - // * the caller must gurantee that sender_hw_addr.len() is <= 255 - // * memory areas guranteed to be non overlapping (buf created in this function). + // * the caller must guarantee that sender_hw_addr.len() is <= 255 + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_hw_addr.as_ptr(), @@ -333,8 +333,8 @@ impl ArpPacket { } { // SAFETY: Safe as - // * the caller must gurantee that target_hw_addr.len() is <= 255 - // * memory areas guranteed to be non overlapping (buf created in this function). + // * the caller must guarantee that target_hw_addr.len() is <= 255 + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_hw_addr.as_ptr(), @@ -365,8 +365,8 @@ impl ArpPacket { } { // SAFETY: Safe as - // * sender_protocol_addr.len() is guranteed to be <= 255 (checked in if above) - // * memory areas guranteed to be non overlapping (buf created in this function). + // * sender_protocol_addr.len() is guaranteed to be <= 255 (checked in if above) + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( sender_protocol_addr.as_ptr(), @@ -377,8 +377,8 @@ impl ArpPacket { } { // SAFETY: Safe as - // * target_protocol_addr.len() is guranteed to be <= 255 (checked in if above) - // * memory areas guranteed to be non overlapping (buf created in this function). + // * target_protocol_addr.len() is guaranteed to be <= 255 (checked in if above) + // * memory areas guaranteed to be non overlapping (buf created in this function). unsafe { core::ptr::copy_nonoverlapping( target_protocol_addr.as_ptr(), diff --git a/etherparse/src/net/arp_packet_slice.rs b/etherparse/src/net/arp_packet_slice.rs index 87f51bf8..bc149585 100644 --- a/etherparse/src/net/arp_packet_slice.rs +++ b/etherparse/src/net/arp_packet_slice.rs @@ -11,7 +11,7 @@ pub struct ArpPacketSlice<'a> { } impl<'a> ArpPacketSlice<'a> { - /// Creates an `ArpPacketSlice` from a slice and verfies that the + /// Creates an `ArpPacketSlice` from a slice and verifies that the /// given slice has enough data to contain an complete ARP packet. pub fn from_slice(slice: &'a [u8]) -> Result, LenError> { if slice.len() < 8 { @@ -82,7 +82,7 @@ impl<'a> ArpPacketSlice<'a> { unsafe { *self.slice.as_ptr().add(4) } } - /// Length (in octets) of internetwork addresses (e.g. 4 for IPv4 or 16 for IPv6). + /// Length (in octets) of network addresses (e.g. 4 for IPv4 or 16 for IPv6). #[inline] pub const fn proto_addr_size(&self) -> u8 { // SAFE: As the constructor verified the length @@ -103,7 +103,7 @@ impl<'a> ArpPacketSlice<'a> { /// Sender hardware address (e.g. MAC address). #[inline] pub const fn sender_hw_addr(&self) -> &[u8] { - // SAFETY: Safe as the constructor verfies the + // SAFETY: Safe as the constructor verifies the // the slice to be at least 8 + hw_addr_size*2 + protocol_addr_size*2 unsafe { core::slice::from_raw_parts(self.slice.as_ptr().add(8), self.hw_addr_size() as usize) @@ -113,7 +113,7 @@ impl<'a> ArpPacketSlice<'a> { /// Sender protocol address (e.g. IPv4 address). #[inline] pub const fn sender_protocol_addr(&self) -> &[u8] { - // SAFETY: Safe as the constructor verfies the + // SAFETY: Safe as the constructor verifies the // the slice to be at least 8 + hw_addr_size*2 + protocol_addr_size*2 unsafe { core::slice::from_raw_parts( @@ -126,7 +126,7 @@ impl<'a> ArpPacketSlice<'a> { /// Target hardware address (e.g. MAC address). #[inline] pub const fn target_hw_addr(&self) -> &[u8] { - // SAFETY: Safe as the constructor verfies the + // SAFETY: Safe as the constructor verifies the // the slice to be at least 8 + hw_addr_size*2 + protocol_addr_size*2 unsafe { core::slice::from_raw_parts( @@ -141,7 +141,7 @@ impl<'a> ArpPacketSlice<'a> { /// Buffer containing the target protocol address (e.g. IPv4 address).. #[inline] pub const fn target_protocol_addr(&self) -> &[u8] { - // SAFETY: Safe as the constructor verfies the + // SAFETY: Safe as the constructor verifies the // the slice to be at least 8 + hw_addr_size*2 + protocol_addr_size*2 unsafe { core::slice::from_raw_parts( @@ -157,8 +157,8 @@ impl<'a> ArpPacketSlice<'a> { #[inline] pub fn to_packet(&self) -> ArpPacket { // SAFETY: Safe as all preconditions of new unchecked - // are fullfilled by the fact that the on the wire packets already - // fullfill them. + // are fulfilled by the fact that the on the wire packets already + // fulfill them. unsafe { ArpPacket::new_unchecked( self.hw_addr_type(), diff --git a/etherparse/src/net/ip_headers.rs b/etherparse/src/net/ip_headers.rs index e8df69a7..bc47104f 100644 --- a/etherparse/src/net/ip_headers.rs +++ b/etherparse/src/net/ip_headers.rs @@ -3,6 +3,36 @@ use crate::err::ip::HeadersWriteError; use crate::err::{Layer, LenError, ValueTooBigError}; use crate::*; +/// Result type of [`crate::IpHeaders::from_slice_lax`]. +/// +/// The result consists of +/// +/// * `.0` the successfully parsed IP headers and extensions. +/// * `.1` the partial or complete payload after the last successfully +/// parsed ip header or ip extensions. +/// * `.2` error why the parsing had to be stopped (filled if there was +/// an error, otherwise `None`). +pub type IpHeadersLaxFromSliceResult<'a> = ( + IpHeaders, + LaxIpPayloadSlice<'a>, + Option<(err::ip_exts::HeadersSliceError, Layer)>, +); + +/// Result type of [`crate::IpHeaders::from_ipv6_slice_lax`]. +/// +/// The result consists of +/// +/// * `.0` the successfully parsed IP headers and extensions. +/// * `.1` the partial or complete payload after the last successfully +/// parsed ip header or ip extensions. +/// * `.2` error why the parsing had to be stopped (filled if there was +/// an error, otherwise `None`). +pub type IpHeadersIpv6LaxFromSliceResult<'a> = ( + IpHeaders, + LaxIpPayloadSlice<'a>, + Option<(err::ipv6_exts::HeaderSliceError, Layer)>, +); + /// Internet protocol headers version 4 & 6. #[derive(Clone, Debug, Eq, PartialEq)] #[allow(clippy::large_enum_variant)] @@ -317,14 +347,7 @@ impl IpHeaders { /// * The value `0`. pub fn from_slice_lax( slice: &[u8], - ) -> Result< - ( - IpHeaders, - LaxIpPayloadSlice<'_>, - Option<(err::ip_exts::HeadersSliceError, Layer)>, - ), - err::ip::LaxHeaderSliceError, - > { + ) -> Result, err::ip::LaxHeaderSliceError> { use err::ip::{HeaderError::*, LaxHeaderSliceError::*}; if slice.is_empty() { @@ -882,14 +905,7 @@ impl IpHeaders { /// * The value `0`. pub fn from_ipv6_slice_lax( slice: &[u8], - ) -> Result< - ( - IpHeaders, - LaxIpPayloadSlice<'_>, - Option<(err::ipv6_exts::HeaderSliceError, Layer)>, - ), - err::ipv6::HeaderSliceError, - > { + ) -> Result, err::ipv6::HeaderSliceError> { // read ipv6 header let (header, header_rest) = Ipv6Header::from_slice(slice)?; diff --git a/etherparse/src/net/ip_slice.rs b/etherparse/src/net/ip_slice.rs index 6b4759d7..5c7eb5f5 100644 --- a/etherparse/src/net/ip_slice.rs +++ b/etherparse/src/net/ip_slice.rs @@ -328,8 +328,8 @@ mod test { use super::*; use crate::test_gens::*; use alloc::{format, vec::Vec}; - use proptest::prelude::*; use core::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + use proptest::prelude::*; #[test] fn debug_clone_eq() { diff --git a/etherparse/src/net/ipv4_ecn.rs b/etherparse/src/net/ipv4_ecn.rs index 5a87921d..f227a490 100644 --- a/etherparse/src/net/ipv4_ecn.rs +++ b/etherparse/src/net/ipv4_ecn.rs @@ -18,10 +18,7 @@ impl Ipv4Ecn { /// Ipv4Ecn with value 0. pub const THREE: Ipv4Ecn = Ipv4Ecn(3); - #[deprecated( - since = "0.18.0", - note = "Please use Ipv4Ecn::THREE instead." - )] + #[deprecated(since = "0.18.0", note = "Please use Ipv4Ecn::THREE instead.")] /// Deprecated, use [`crate::Ipv4Ecn::THREE`] instead. pub const TRHEE: Ipv4Ecn = Ipv4Ecn::THREE; diff --git a/etherparse/src/net/ipv4_header.rs b/etherparse/src/net/ipv4_header.rs index b29e7697..9854b5b3 100644 --- a/etherparse/src/net/ipv4_header.rs +++ b/etherparse/src/net/ipv4_header.rs @@ -379,11 +379,11 @@ impl Ipv4Header { /// Read an Ipv4Header from a slice and return the header & unused parts /// of the slice. /// - /// Note that this function DOES NOT seperate the payload based on the + /// Note that this function DOES NOT separate the payload based on the /// `total_length` field present in the IPv4 header. It just returns the /// left over slice after the header. /// - /// If you want to have correctly seperated payload including the IP extension + /// If you want to have correctly separated payload including the IP extension /// headers use /// /// * [`IpHeaders::from_ipv4_slice`] (decodes all the fields of the IP headers) @@ -586,7 +586,7 @@ impl Ipv4Header { header_raw } - /// Write the given header with the checksum and header length specified in the seperate arguments + /// Write the given header with the checksum and header length specified in the separate arguments #[cfg(feature = "std")] fn write_ipv4_header_internal( &self, diff --git a/etherparse/src/net/ipv4_header_slice.rs b/etherparse/src/net/ipv4_header_slice.rs index 81dda752..01c4a351 100644 --- a/etherparse/src/net/ipv4_header_slice.rs +++ b/etherparse/src/net/ipv4_header_slice.rs @@ -1,5 +1,5 @@ -use core::slice::from_raw_parts; use core::net::Ipv4Addr; +use core::slice::from_raw_parts; use crate::*; @@ -13,7 +13,7 @@ impl<'a> Ipv4HeaderSlice<'a> { /// Creates a slice containing an ipv4 header (including header options). /// /// If you also want to have the payload & ip extension headers correctly - /// seperated you can use + /// separated you can use /// /// * [`crate::Ipv4Slice::from_slice`] (just identifies slice ranges) /// * [`crate::IpHeaders::from_ipv4_slice`] (unpacks all fields) @@ -123,7 +123,7 @@ impl<'a> Ipv4HeaderSlice<'a> { // SAFETY: // get_unchecked: Safe as the slice length is checked to be at least // Ipv4Header::MIN_LEN (20) in the constructor. - // new_unchecked: Safe as the bitshift by 2 guarantees that the passed + // new_unchecked: Safe as the bit-shift by 2 guarantees that the passed // value is not bigger then 6 bits. unsafe { Ipv4Dscp::new_unchecked(*self.slice.get_unchecked(1) >> 2) } } @@ -134,7 +134,7 @@ impl<'a> Ipv4HeaderSlice<'a> { // SAFETY: // get_unchecked: Safe as the slice length is checked to be at least // Ipv4Header::MIN_LEN (20) in the constructor. - // new_unchecked: Safe as value has been bitmasked to two bits. + // new_unchecked: Safe as value has been bit-masked to two bits. unsafe { Ipv4Ecn::new_unchecked(*self.slice.get_unchecked(1) & 0b0000_0011) } } @@ -217,7 +217,7 @@ impl<'a> Ipv4HeaderSlice<'a> { unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) } } - /// Read the "dont fragment" flag from the slice. + /// Read the "don't fragment" flag from the slice. #[inline] pub fn dont_fragment(&self) -> bool { // SAFETY: diff --git a/etherparse/src/net/lax_ip_slice.rs b/etherparse/src/net/lax_ip_slice.rs index 63de9772..928102ee 100644 --- a/etherparse/src/net/lax_ip_slice.rs +++ b/etherparse/src/net/lax_ip_slice.rs @@ -6,7 +6,7 @@ use crate::{err::*, *}; /// payload to be incomplete/cut off and errors to be present in /// the IpPayload. /// -/// The main usecases for "laxly" parsed slices are are: +/// The main use cases for "laxly" parsed slices are are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. @@ -91,12 +91,12 @@ impl<'a> LaxIpSlice<'a> { /// Separates IP headers (include extension headers) & the IP payload from the given slice /// as far as possible without encountering an error and with less strict length checks. - /// This function is usefull for cut off packet or for packets with unset length fields. + /// This function is useful for cut off packet or for packets with unset length fields. /// /// If you want to only receive correct IpPayloads use [`IpSlice::from_slice`] /// instead. /// - /// The main usecases for this functions are: + /// The main use cases for this functions are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. @@ -113,29 +113,29 @@ impl<'a> LaxIpSlice<'a> { /// with the successfully parsed parts and the error as optional. Only if an /// unrecoverable error is encountered in the IP header itself an `Err` is returned. /// In the normal `from_slice` function an `Err` is returned if an error is - /// encountered in an exteions header. + /// encountered in an extension header. /// * `from_slice_lax` ignores inconsistent `total_len` (in IPv4 headers) and /// inconsistent `payload_length` (in IPv6 headers) values. When these length - /// values in the IP header are inconsistant the length of the given slice is + /// values in the IP header are inconsistent the length of the given slice is /// used as a substitute. /// - /// You can check if the slice length was used as a substitude by checking + /// You can check if the slice length was used as a substitute by checking /// if `result.payload().len_source` is set to [`LenSource::Slice`]. /// If a substitution was not needed `len_source` is set to /// [`LenSource::Ipv4HeaderTotalLen`] or [`LenSource::Ipv6HeaderPayloadLen`]. /// /// # When is the slice length used as a fallback? /// - /// For IPv4 packets the slice length is used as a fallback/substitude + /// For IPv4 packets the slice length is used as a fallback/substitute /// if the `total_length` field in the IPv4 header is: /// - /// * Bigger then the given slice (payload cannot fully be seperated). + /// * Bigger then the given slice (payload cannot fully be separated). /// * Too small to contain at least the IPv4 header. /// - /// For IPv6 packet the slice length is used as a fallback/substitude + /// For IPv6 packet the slice length is used as a fallback/substitute /// if the `payload_length` is /// - /// * Bigger then the given slice (payload cannot fully be seperated). + /// * Bigger then the given slice (payload cannot fully be separated). /// * The value `0`. pub fn from_slice( slice: &[u8], @@ -237,7 +237,7 @@ impl<'a> LaxIpSlice<'a> { // SAFETY: Safe as slice.len() >= header_len was validated // in a if statement above. slice.as_ptr().add(header_len), - // SAFETY: Safe as total_length >= header_len was verfied in an + // SAFETY: Safe as total_length >= header_len was verified in an // if statement above as well as that slice.len() >= total_length_usize. total_len - header_len, ) @@ -349,7 +349,7 @@ impl<'a> LaxIpSlice<'a> { let payload_len = usize::from(header.payload_length()); let (header_payload, len_source, incomplete) = if 0 == payload_len && slice.len() > Ipv6Header::LEN { - // zero set as payload len, assume jumbograms or unitialized + // zero set as payload len, assume jumbograms or uninitialized // length and use the slice length as a fallback value // TODO: Add payload length parsing from the jumbogram for the zero case ( @@ -436,8 +436,8 @@ mod test { use super::*; use crate::test_gens::*; use alloc::{format, vec::Vec}; - use proptest::prelude::*; use core::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + use proptest::prelude::*; #[test] fn debug_clone_eq() { @@ -1089,7 +1089,7 @@ mod test { ipv6_exts.hop_by_hop_options.as_ref().map(|h| h.header_len()).unwrap_or(0) + ipv6_exts.destination_options.as_ref().map(|h| h.header_len()).unwrap_or(0) + ipv6_exts.routing.as_ref().map(|h| h.routing.header_len()).unwrap_or(0) + - // routing.final_destination_options skiped, as after auth + // routing.final_destination_options skipped, as after auth ipv6_exts.fragment.as_ref().map(|h| h.header_len()).unwrap_or(0); // inject length zero into auth header (not valid, will diff --git a/etherparse/src/net/lax_ipv4_slice.rs b/etherparse/src/net/lax_ipv4_slice.rs index d9359c75..a9082da0 100644 --- a/etherparse/src/net/lax_ipv4_slice.rs +++ b/etherparse/src/net/lax_ipv4_slice.rs @@ -6,7 +6,7 @@ use crate::*; /// payload to be incomplete/cut off and errors to be present in /// the IpPayload. /// -/// The main usecases for "laxly" parsed slices are are: +/// The main use cases for "laxly" parsed slices are are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. @@ -29,7 +29,7 @@ impl<'a> LaxIpv4Slice<'a> { /// If you want to only receive correct IpPayloads use [`Ipv4Slice::from_slice`] /// instead. /// - /// The main usecases for this functions are: + /// The main use cases for this functions are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. @@ -46,9 +46,9 @@ impl<'a> LaxIpv4Slice<'a> { /// with the successfully parsed parts and the error as optional. Only if an /// unrecoverable error is encountered in the IP header itself an `Err` is returned. /// In the normal `Ipv4Slice::from_slice` function an `Err` is returned if an error is - /// encountered in an exteions header. + /// encountered in an extension header. /// * `LaxIpv4Slice::from_slice` ignores inconsistent `total_len` values. When the `total_len` - /// value in the IPv4 header are inconsistant the length of the given slice is + /// value in the IPv4 header are inconsistent the length of the given slice is /// used as a substitute. /// /// ## What happens in the `total_len` value is inconsistent? @@ -57,14 +57,14 @@ impl<'a> LaxIpv4Slice<'a> { /// length of the given slice is used as a substitute. This can happen /// if the `total_length` field in the IPv4 header is: /// - /// * Bigger then the given slice (payload cannot fully be seperated). + /// * Bigger then the given slice (payload cannot fully be separated). /// * Too small to contain at least the IPv4 header. /// /// Additionally you can check if more data was expected based on the /// `total_len` but the given slice was too small by checking if `incomplete` /// is set to `true` in the returned [`LaxIpPayloadSlice`]. /// - /// You can check if the slice length was used as a substitude by checking + /// You can check if the slice length was used as a substitute by checking /// if the `len_source` value in the returned [`LaxIpPayloadSlice`] is set to /// [`LenSource::Slice`]. If a substitution was not needed `len_source` /// is set to [`LenSource::Ipv4HeaderTotalLen`]. @@ -338,6 +338,7 @@ mod test { payload: &payload } ); + assert_eq!(actual.payload_ip_number(), header.next_header().unwrap()); } // error len smaller then min header len diff --git a/etherparse/src/net/lax_ipv6_slice.rs b/etherparse/src/net/lax_ipv6_slice.rs index e0b08f5f..56a0b8f9 100644 --- a/etherparse/src/net/lax_ipv6_slice.rs +++ b/etherparse/src/net/lax_ipv6_slice.rs @@ -9,7 +9,7 @@ use crate::{ /// payload to incomplete/cut off and errors to be present in /// the IpPayload. /// -/// The main usecases for "laxly" parsed slices are are: +/// The main use cases for "laxly" parsed slices are are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. @@ -25,14 +25,14 @@ pub struct LaxIpv6Slice<'a> { } impl<'a> LaxIpv6Slice<'a> { - /// Seperate an IPv6 header (+ extensions) & the payload from the given slice with + /// separate an IPv6 header (+ extensions) & the payload from the given slice with /// less strict length checks (useful for cut off packet or for packets with /// unset length fields). /// /// If you want to only receive correct IpPayloads use [`crate::Ipv4Slice::from_slice`] /// instead. /// - /// The main usecases for this functions are: + /// The main use cases for this functions are: /// /// * Parsing packets that have been cut off. This is, for example, useful to /// parse packets returned via ICMP as these usually only contain the start. @@ -49,22 +49,22 @@ impl<'a> LaxIpv6Slice<'a> { /// with the successfully parsed parts and the error as optional. Only if an /// unrecoverable error is encountered in the IP header itself an `Err` is returned. /// In the normal `Ipv4Slice::from_slice` function an `Err` is returned if an error is - /// encountered in an exteions header. + /// encountered in an extension header. /// * `LaxIpv4Slice::from_slice` ignores inconsistent `payload_length` values. When the - /// `payload_length` value in the IPv6 header is inconsistant the length of + /// `payload_length` value in the IPv6 header is inconsistent the length of /// the given slice is used as a substitute. /// - /// You can check if the slice length was used as a substitude by checking + /// You can check if the slice length was used as a substitute by checking /// if the `len_source` value in the returned [`IpPayloadSlice`] is set to /// [`LenSource::Slice`]. If a substitution was not needed `len_source` /// is set to [`LenSource::Ipv6HeaderPayloadLen`]. /// /// # When is the slice length used as a fallback? /// - /// The slice length is used as a fallback/substitude if the `payload_length` + /// The slice length is used as a fallback/substitute if the `payload_length` /// field in the IPv6 header is /// - /// * Bigger then the given slice (payload cannot fully be seperated). + /// * Bigger then the given slice (payload cannot fully be separated). /// * The value `0`. pub fn from_slice( slice: &'a [u8], diff --git a/etherparse/src/net/net_headers.rs b/etherparse/src/net/net_headers.rs index 3389fd78..17b61a47 100644 --- a/etherparse/src/net/net_headers.rs +++ b/etherparse/src/net/net_headers.rs @@ -18,12 +18,35 @@ pub enum NetHeaders { impl NetHeaders { /// Returns true if the NetHeaders contains either IPv4 or IPv6. + #[inline] pub fn is_ip(&self) -> bool { use NetHeaders::*; matches!(self, Ipv4(_, _) | Ipv6(_, _)) } + /// Returns true if the NetHeaders contains IPv4. + #[inline] + pub fn is_ipv4(&self) -> bool { + use NetHeaders::*; + matches!(self, Ipv4(_, _)) + } + + /// Returns true if the NetHeaders contains IPv6. + #[inline] + pub fn is_ipv6(&self) -> bool { + use NetHeaders::*; + matches!(self, Ipv6(_, _)) + } + + /// Returns true if the NetHeaders contains ARP. + #[inline] + pub fn is_arp(&self) -> bool { + use NetHeaders::*; + matches!(self, Arp(_)) + } + /// Returns references to the IPv4 header & extensions if the header contains IPv4 values. + #[inline] pub fn ipv4_ref(&self) -> Option<(&Ipv4Header, &Ipv4Extensions)> { if let NetHeaders::Ipv4(header, exts) = self { Some((header, exts)) @@ -33,6 +56,7 @@ impl NetHeaders { } /// Returns references to the IPv6 header & extensions if the header contains IPv6 values. + #[inline] pub fn ipv6_ref(&self) -> Option<(&Ipv6Header, &Ipv6Extensions)> { if let NetHeaders::Ipv6(header, exts) = self { Some((header, exts)) @@ -41,6 +65,16 @@ impl NetHeaders { } } + /// Returns references to the ARP packet if the header contains ARP values. + #[inline] + pub fn arp_ref(&self) -> Option<&ArpPacket> { + if let NetHeaders::Arp(arp) = self { + Some(arp) + } else { + None + } + } + /// Sets all the next_header fields in the ipv4 & ipv6 header /// as well as in all extension headers and returns the ether /// type number. @@ -59,7 +93,7 @@ impl NetHeaders { } Ipv6(ref mut header, ref mut extensions) => { header.next_header = extensions.set_next_headers(last_next_header); - Ok(EtherType::IPV4) + Ok(EtherType::IPV6) } Arp(_) => Err(err::net::NetSetNextHeaderError::ArpHeader), } @@ -117,13 +151,19 @@ mod tests { } #[test] - fn ipv4_ref() { + fn ipv4_ipv6_arp_refs_and_is() { // ipv4 { let h: Ipv4Header = Default::default(); let e: Ipv4Extensions = Default::default(); let s = NetHeaders::Ipv4(h.clone(), e.clone()); assert_eq!(s.ipv4_ref(), Some((&h, &e))); + assert_eq!(s.ipv6_ref(), None); + assert_eq!(s.arp_ref(), None); + assert!(s.is_ip()); + assert!(s.is_ipv4()); + assert_eq!(false, s.is_ipv6()); + assert_eq!(false, s.is_arp()); } // ipv6 { @@ -131,24 +171,72 @@ mod tests { let e: Ipv6Extensions = Default::default(); let s = NetHeaders::Ipv6(h.clone(), e.clone()); assert_eq!(s.ipv4_ref(), None); + assert_eq!(s.ipv6_ref(), Some((&h, &e))); + assert_eq!(s.arp_ref(), None); + assert!(s.is_ip()); + assert_eq!(false, s.is_ipv4()); + assert!(s.is_ipv6()); + assert_eq!(false, s.is_arp()); + } + // arp + { + let h: ArpPacket = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0; 6], + &[0; 4], + &[0; 6], + &[0; 4], + ) + .unwrap(); + let s = NetHeaders::Arp(h.clone()); + assert_eq!(s.ipv4_ref(), None); + assert_eq!(s.ipv6_ref(), None); + assert_eq!(s.arp_ref(), Some(&h)); + assert_eq!(false, s.is_ip()); + assert_eq!(false, s.is_ipv4()); + assert_eq!(false, s.is_ipv6()); + assert!(s.is_arp()); } } #[test] - fn ipv6_ref() { + fn try_set_next_headers() { // ipv4 { let h: Ipv4Header = Default::default(); let e: Ipv4Extensions = Default::default(); - let s = NetHeaders::Ipv4(h.clone(), e.clone()); - assert_eq!(s.ipv6_ref(), None); + let mut s = NetHeaders::Ipv4(h.clone(), e.clone()); + assert_eq!(s.try_set_next_headers(IpNumber::UDP), Ok(EtherType::IPV4)); + assert_eq!(s.ipv4_ref().unwrap().0.protocol, IpNumber::UDP); } // ipv6 { let h: Ipv6Header = Default::default(); let e: Ipv6Extensions = Default::default(); - let s = NetHeaders::Ipv6(h.clone(), e.clone()); - assert_eq!(s.ipv6_ref(), Some((&h, &e))); + let mut s = NetHeaders::Ipv6(h.clone(), e.clone()); + assert_eq!(s.try_set_next_headers(IpNumber::UDP), Ok(EtherType::IPV6)); + assert_eq!(s.ipv6_ref().unwrap().0.next_header, IpNumber::UDP); + } + // arp + { + let h: ArpPacket = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0; 6], + &[0; 4], + &[0; 6], + &[0; 4], + ) + .unwrap(); + let mut s = NetHeaders::Arp(h.clone()); + assert_eq!( + s.try_set_next_headers(IpNumber::UDP), + Err(err::net::NetSetNextHeaderError::ArpHeader) + ); + assert_eq!(s.arp_ref().unwrap(), &h); } } @@ -168,6 +256,21 @@ mod tests { let s = NetHeaders::Ipv6(h.clone(), e.clone()); assert_eq!(s.header_len(), h.header_len() + e.header_len()); } + // arp + { + let h: ArpPacket = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0; 6], + &[0; 4], + &[0; 6], + &[0; 4], + ) + .unwrap(); + let s = NetHeaders::Arp(h.clone()); + assert_eq!(s.header_len(), h.packet_len(),); + } } #[test] @@ -188,5 +291,20 @@ mod tests { let a: NetHeaders = s.clone().into(); assert_eq!(a, NetHeaders::Ipv6(h.clone(), e.clone())); } + // arp + { + let h: ArpPacket = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0; 6], + &[0; 4], + &[0; 6], + &[0; 4], + ) + .unwrap(); + let a: NetHeaders = h.clone().into(); + assert_eq!(a, NetHeaders::Arp(h)); + } } } diff --git a/etherparse/src/net/net_slice.rs b/etherparse/src/net/net_slice.rs index c0a1c1be..f7d20f72 100644 --- a/etherparse/src/net/net_slice.rs +++ b/etherparse/src/net/net_slice.rs @@ -27,6 +27,57 @@ impl<'a> NetSlice<'a> { matches!(self, Ipv4(_) | Ipv6(_)) } + /// Returns true if the NetSlice contains IPv4. + #[inline] + pub fn is_ipv4(&self) -> bool { + use NetSlice::*; + matches!(self, Ipv4(_)) + } + + /// Returns true if the NetSlice contains IPv6. + #[inline] + pub fn is_ipv6(&self) -> bool { + use NetSlice::*; + matches!(self, Ipv6(_)) + } + + /// Returns true if the NetSlice contains ARP. + #[inline] + pub fn is_arp(&self) -> bool { + use NetSlice::*; + matches!(self, Arp(_)) + } + + /// Returns references to the IPv4 slice if the slice contains an IPv4 values. + #[inline] + pub fn ipv4_ref(&self) -> Option<&Ipv4Slice<'a>> { + if let NetSlice::Ipv4(s) = self { + Some(s) + } else { + None + } + } + + /// Returns references to the IPv6 slice if the slice contains an IPv6 values. + #[inline] + pub fn ipv6_ref(&self) -> Option<&Ipv6Slice<'a>> { + if let NetSlice::Ipv6(s) = self { + Some(s) + } else { + None + } + } + + /// Returns references to the ARP packet slice if the slice contains an ARP values. + #[inline] + pub fn arp_ref(&self) -> Option<&ArpPacketSlice> { + if let NetSlice::Arp(arp) = self { + Some(arp) + } else { + None + } + } + /// Returns a reference to ip payload if the net slice contains /// an ipv4 or ipv6 slice. #[inline] @@ -92,7 +143,7 @@ mod tests { } #[test] - fn ip_payload_ref() { + fn ip_payload_ref_and_is_ip() { // ipv4 { let payload = [1, 2, 3, 4]; @@ -109,7 +160,8 @@ mod tests { bytes.extend_from_slice(&payload); bytes }; - let s = NetSlice::Ipv4(Ipv4Slice::from_slice(&bytes).unwrap()); + let p = Ipv4Slice::from_slice(&bytes).unwrap(); + let s = NetSlice::Ipv4(p.clone()); assert_eq!( s.ip_payload_ref(), Some(&IpPayloadSlice { @@ -119,6 +171,13 @@ mod tests { payload: &payload }) ); + assert!(s.is_ip()); + assert!(s.is_ipv4()); + assert_eq!(false, s.is_ipv6()); + assert_eq!(false, s.is_arp()); + assert_eq!(Some(&p), s.ipv4_ref()); + assert_eq!(None, s.ipv6_ref()); + assert_eq!(None, s.arp_ref()); } // ipv6 { @@ -136,7 +195,8 @@ mod tests { bytes.extend_from_slice(&payload); bytes }; - let s = NetSlice::Ipv6(Ipv6Slice::from_slice(&bytes).unwrap()); + let p = Ipv6Slice::from_slice(&bytes).unwrap(); + let s = NetSlice::Ipv6(p.clone()); assert_eq!( s.ip_payload_ref(), Some(&IpPayloadSlice { @@ -146,6 +206,37 @@ mod tests { payload: &payload }) ); + assert!(s.is_ip()); + assert_eq!(false, s.is_ipv4()); + assert!(s.is_ipv6()); + assert_eq!(false, s.is_arp()); + assert_eq!(None, s.ipv4_ref()); + assert_eq!(Some(&p), s.ipv6_ref()); + assert_eq!(None, s.arp_ref()); + } + // arp + { + let arp = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0; 6], + &[0; 4], + &[0; 6], + &[0; 4], + ) + .unwrap(); + let bytes = arp.to_bytes(); + let p = ArpPacketSlice::from_slice(&bytes).unwrap(); + let s = NetSlice::Arp(p.clone()); + assert_eq!(None, s.ip_payload_ref()); + assert_eq!(false, s.is_ip()); + assert_eq!(false, s.is_ipv4()); + assert_eq!(false, s.is_ipv6()); + assert!(s.is_arp()); + assert_eq!(None, s.ipv4_ref()); + assert_eq!(None, s.ipv6_ref()); + assert_eq!(Some(&p), s.arp_ref()); } } diff --git a/etherparse/src/packet_builder.rs b/etherparse/src/packet_builder.rs index 50db0f60..0036dfd1 100644 --- a/etherparse/src/packet_builder.rs +++ b/etherparse/src/packet_builder.rs @@ -148,7 +148,7 @@ impl PacketBuilder { } } - /// Start an packet with an Linux Cooked Catpure (v1) header. + /// Start an packet with an Linux Cooked Capture (v1) header. /// /// # Example /// @@ -189,7 +189,7 @@ impl PacketBuilder { arp_hrd_type: ArpHardwareId::ETHERNET, sender_address_valid_length, sender_address, - protocol_type: LinuxSllProtocolType::EtherType(EtherType(0)), // Will be overwitten when writing depending on the net layer + protocol_type: LinuxSllProtocolType::EtherType(EtherType(0)), // Will be overwritten when writing depending on the net layer })), vlan_header: None, net_header: None, @@ -1948,7 +1948,8 @@ fn final_write_with_net( value.outer.ether_type = ether_type::VLAN_TAGGED_FRAME; value.inner.ether_type = net_ether_type; //serialize - value.write(writer).map_err(Io)?; + value.outer.write(writer).map_err(Io)?; + value.inner.write(writer).map_err(Io)?; } None => {} } @@ -2067,7 +2068,7 @@ fn final_size(builder: &PacketBuilderStep, payload_size: usize) -> usize { None => 0, }) + match builder.state.vlan_header { Some(Single(_)) => SingleVlanHeader::LEN, - Some(Double(_)) => DoubleVlanHeader::LEN, + Some(Double(_)) => SingleVlanHeader::LEN * 2, None => 0, } + match builder.state.net_header { Some(Ipv4(ref value, ref ext)) => value.header_len() + ext.header_len(), @@ -2967,7 +2968,7 @@ mod test { //check the deserialized size assert_eq!( Ethernet2Header::LEN - + DoubleVlanHeader::LEN + + SingleVlanHeader::LEN * 2 + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(), @@ -3635,7 +3636,11 @@ mod test { //ipv6 double vlan ethernet assert_eq!( - Ethernet2Header::LEN + DoubleVlanHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + 123, + Ethernet2Header::LEN + + SingleVlanHeader::LEN * 2 + + Ipv6Header::LEN + + UdpHeader::LEN + + 123, PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) .double_vlan(0x123.try_into().unwrap(), 0x234.try_into().unwrap()) .ipv6( diff --git a/etherparse/src/packet_headers.rs b/etherparse/src/packet_headers.rs index 2f0ba58c..4aa4e98f 100644 --- a/etherparse/src/packet_headers.rs +++ b/etherparse/src/packet_headers.rs @@ -1,6 +1,6 @@ -use crate::err::LenError; - use super::*; +use crate::err::LenError; +use arrayvec::ArrayVec; /// Decoded packet headers (data link layer and lower). /// @@ -16,17 +16,24 @@ use super::*; pub struct PacketHeaders<'a> { /// Ethernet II header if present. pub link: Option, - /// Single or double vlan headers if present. - pub vlan: Option, + + /// Link extension headers (VLAN & MAC Sec headers). + pub link_exts: ArrayVec, + /// IPv4 or IPv6 header and IP extension headers if present. pub net: Option, + /// TCP or UDP header if present. pub transport: Option, + /// Payload of the last parsed layer. pub payload: PayloadSlice<'a>, } impl<'a> PacketHeaders<'a> { + /// Maximum supported number of link extensions headers. + pub const LINK_EXTS_CAP: usize = 3; + /// 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. @@ -62,8 +69,8 @@ impl<'a> PacketHeaders<'a> { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } /// } @@ -133,8 +140,8 @@ impl<'a> PacketHeaders<'a> { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } /// } @@ -158,54 +165,90 @@ impl<'a> PacketHeaders<'a> { let mut result = PacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, payload: PayloadSlice::Ether(EtherPayloadSlice { ether_type, + len_source: LenSource::Slice, payload: rest, }), }; use ether_type::*; + let mut len_source = LenSource::Slice; + loop { + match ether_type { + VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { + if result.link_exts.is_full() { + break; + } + let (vlan, vlan_rest) = match SingleVlanHeader::from_slice(rest) { + Ok(v) => v, + Err(err) => { + return Err(Len(err.add_offset(slice.len() - rest.len()))); + } + }; + // set the rest & ether_type for the following operations + rest = vlan_rest; + ether_type = vlan.ether_type; + result.payload = PayloadSlice::Ether(EtherPayloadSlice { + ether_type, + len_source, + payload: rest, + }); + // SAFETY: Safe as the while loop condition verifies that there is space left. + unsafe { + result.link_exts.push_unchecked(LinkExtHeader::Vlan(vlan)); + } + } + MACSEC => { + use err::macsec::HeaderSliceError as I; + if result.link_exts.is_full() { + break; + } + let macsec = match MacsecSlice::from_slice(rest) { + Ok(v) => v, + Err(I::Len(err)) => { + return Err(Len(err.add_offset(slice.len() - rest.len()))); + } + Err(I::Content(err)) => { + return Err(Macsec(err)); + } + }; - 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, - }); + // SAFETY: Safe as the while loop condition verifies that there is space left. + unsafe { + result + .link_exts + .push_unchecked(LinkExtHeader::Macsec(macsec.header.to_header())); + } - //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 })) + use MacsecPayloadSlice::*; + match macsec.payload { + Unmodified(e) => { + rest = e.payload; + ether_type = e.ether_type; + if e.len_source != LenSource::Slice { + len_source = e.len_source; + } + result.payload = PayloadSlice::Ether(EtherPayloadSlice { + ether_type: e.ether_type, + len_source, + payload: e.payload, + }); + } + Modified(m) => { + result.payload = PayloadSlice::MacsecMod(m); + return Ok(result); + } } - //no second vlan header detected -> single vlan header - _ => Some(Single(outer)), + } + _ => { + break; } } - //no vlan header - _ => None, - }; + } // parse ip match ether_type { @@ -311,8 +354,8 @@ impl<'a> PacketHeaders<'a> { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } /// } @@ -335,7 +378,7 @@ impl<'a> PacketHeaders<'a> { let mut result = PacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: Some(ip_header.into()), transport: None, payload: PayloadSlice::Ip(ip_payload.clone()), @@ -365,6 +408,38 @@ impl<'a> PacketHeaders<'a> { Ok(result) } + + /// Returns the first two VLAN headers. + pub fn vlan(&self) -> Option { + let mut result = None; + for ext in &self.link_exts { + if let LinkExtHeader::Vlan(s) = ext { + if let Some(outer) = result { + return Some(VlanHeader::Double(DoubleVlanHeader { + outer, + inner: s.clone(), + })); + } else { + result = Some(s.clone()); + } + } + } + result.map(VlanHeader::Single) + } + + /// Returns the VLAN ids present in this packet. + pub fn vlan_ids(&self) -> ArrayVec { + let mut result = ArrayVec::::new_const(); + for e in &self.link_exts { + if let LinkExtHeader::Vlan(s) = e { + // SAFETY: Safe as the vlan ids array has the same size as slice.link_exts. + unsafe { + result.push_unchecked(s.vlan_id); + } + } + } + result + } } /// helper function to process transport headers @@ -429,6 +504,7 @@ fn read_transport( mod test { use super::*; use crate::err::packet::SliceError; + use crate::err::Layer; use crate::test_packet::TestPacket; const VLAN_ETHER_TYPES: [EtherType; 3] = [ @@ -436,26 +512,28 @@ mod test { ether_type::PROVIDER_BRIDGING, ether_type::VLAN_DOUBLE_TAGGED_FRAME, ]; + const MACSEC_ETHER_TYPES: [EtherType; 1] = [ether_type::MACSEC]; #[test] fn debug() { use alloc::format; let header = PacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, payload: PayloadSlice::Ether(EtherPayloadSlice { ether_type: EtherType(0), + len_source: LenSource::Slice, payload: &[], }), }; assert_eq!( &format!("{:?}", header), &format!( - "PacketHeaders {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?}, payload: {:?} }}", + "PacketHeaders {{ link: {:?}, link_exts: {:?}, net: {:?}, transport: {:?}, payload: {:?} }}", header.link, - header.vlan, + header.link_exts, header.net, header.transport, header.payload @@ -467,23 +545,160 @@ mod test { fn clone_eq() { let header = PacketHeaders { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, payload: PayloadSlice::Ether(EtherPayloadSlice { ether_type: EtherType(0), + len_source: LenSource::Slice, payload: &[], }), }; assert_eq!(header.clone(), header); } + #[test] + fn vlan_vlan_ids() { + // no content + { + let headers = PacketHeaders { + link: None, + link_exts: ArrayVec::new_const(), + net: None, + transport: None, + payload: PayloadSlice::Empty, + }; + assert_eq!(headers.vlan(), None); + assert_eq!(headers.vlan_ids(), ArrayVec::::new_const()); + } + + // single vlan header + { + let outer = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let headers = PacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Vlan(outer.clone())); + exts + }, + net: None, + transport: None, + payload: PayloadSlice::Empty, + }; + + assert_eq!(headers.vlan(), Some(VlanHeader::Single(outer.clone()))); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids + }); + } + + // two vlan header + { + let outer = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + }; + let inner = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let headers = PacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Vlan(outer.clone())); + exts.push(LinkExtHeader::Vlan(inner.clone())); + exts + }, + net: None, + transport: None, + payload: PayloadSlice::Empty, + }; + + assert_eq!( + headers.vlan(), + Some(VlanHeader::Double(DoubleVlanHeader { + outer: outer.clone(), + inner: inner.clone(), + })) + ); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // three vlan header + { + let vlan1 = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + }; + let vlan2 = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let vlan3 = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(3).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + }; + let headers = PacketHeaders { + link: None, + link_exts: { + let mut exts = ArrayVec::new_const(); + exts.push(LinkExtHeader::Vlan(vlan1.clone())); + exts.push(LinkExtHeader::Vlan(vlan2.clone())); + exts.push(LinkExtHeader::Vlan(vlan3.clone())); + exts + }, + net: None, + transport: None, + payload: PayloadSlice::Empty, + }; + + assert_eq!( + headers.vlan(), + Some(VlanHeader::Double(DoubleVlanHeader { + outer: vlan1.clone(), + inner: vlan2.clone(), + })) + ); + assert_eq!(headers.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids.push(VlanId::try_new(3).unwrap()); + ids + }); + } + } + #[test] fn from_x_slice() { // no eth - from_x_slice_vlan_variants(&TestPacket { + from_x_slice_link_exts_variants(&TestPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }); @@ -497,13 +712,13 @@ mod test { }; let test = TestPacket { link: Some(LinkHeader::Ethernet2(eth.clone())), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; // ok ethernet header (with unknown next) - from_x_slice_vlan_variants(&test); + from_x_slice_link_exts_variants(&test); // eth len error { @@ -523,99 +738,175 @@ mod test { } } - fn from_x_slice_vlan_variants(base: &TestPacket) { - // none - from_x_slice_ip_variants(base); + fn from_x_slice_link_exts_variants(base: &TestPacket) { + #[derive(Copy, Clone, Eq, PartialEq)] + enum Ext { + Macsec, + VlanTaggedFrame, + VlanDoubleTaggedFrame, + ProviderBridging, + } - // 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(), - }; + impl Ext { + pub fn ether_type(&self) -> EtherType { + match self { + Ext::Macsec => EtherType::MACSEC, + Ext::VlanTaggedFrame => EtherType::VLAN_TAGGED_FRAME, + Ext::VlanDoubleTaggedFrame => EtherType::VLAN_DOUBLE_TAGGED_FRAME, + Ext::ProviderBridging => EtherType::PROVIDER_BRIDGING, + } + } - for vlan_ether_type in VLAN_ETHER_TYPES { + pub fn add(&self, base: &TestPacket) -> TestPacket { let mut test = base.clone(); - test.set_ether_type(vlan_ether_type); - test.vlan = Some(VlanHeader::Single(single.clone())); + test.set_ether_type(self.ether_type()); + test.link_exts + .try_push(match self { + Ext::Macsec => LinkExtHeader::Macsec(MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType(3)), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }), + Ext::VlanTaggedFrame + | Ext::VlanDoubleTaggedFrame + | Ext::ProviderBridging => LinkExtHeader::Vlan(SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: 3.into(), + }), + }) + .unwrap(); + test + } + } - // ok vlan header - from_x_slice_ip_variants(&test); + let test_macsec_mod = |test: &TestPacket| { + for ptype in [ + MacsecPType::Modified, + MacsecPType::Encrypted, + MacsecPType::EncryptedUnmodified, + ] { + let mut test = test.clone(); + if let Some(LinkExtHeader::Macsec(m)) = test.link_exts.last_mut() { + m.ptype = ptype; + } + if matches!(test.link_exts.last(), Some(LinkExtHeader::Macsec(_))) { + from_x_slice_assert_ok(&test); + } + } + }; - // len error - { - let data = test.to_vec(&[]); - for len in 0..single.header_len() { - let base_len = test.len(&[]) - single.header_len(); + let len_errors = |test: &TestPacket| { + let data = test.to_vec(&[]); + let req_len = test.link_exts.last().unwrap().header_len(); + for len in 0..req_len { + let base_len = test.len(&[]) - req_len; + + let (err_req_len, err_layer) = match test.link_exts.last().unwrap() { + LinkExtHeader::Vlan(h) => (h.header_len(), Layer::VlanHeader), + LinkExtHeader::Macsec(_) => { + if len < 6 { + (6, Layer::MacsecHeader) + } else { + (req_len, Layer::MacsecHeader) + } + } + }; - 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()), - ); + let mut len_source = LenSource::Slice; + for prev_exts in test.link_exts.iter().rev().skip(1) { + if let LinkExtHeader::Macsec(m) = prev_exts { + if m.short_len != MacsecShortLen::ZERO { + len_source = LenSource::MacsecShortLength; + } } } - } - } - // 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 err = LenError { + required_len: err_req_len, + len, + len_source, + layer: err_layer, + layer_start_offset: base_len, }; - let mut test = base.clone(); - test.set_ether_type(outer_vlan_ether_type); - test.vlan = Some(VlanHeader::Double(double.clone())); + from_slice_assert_err(&test, &data[..base_len + len], SliceError::Len(err.clone())); + } + }; - // ok double vlan header - from_x_slice_ip_variants(&test); + let content_errors = |test: &TestPacket| { + if let Some(LinkExtHeader::Macsec(last)) = test.link_exts.last() { + let mut data = test.to_vec(&[]); - // len error - { - let data = test.to_vec(&[]); - for len in 0..SingleVlanHeader::LEN { - let base_len = test.len(&[]) - SingleVlanHeader::LEN; + // inject bad version id + let macsec_offset = data.len() - last.header_len(); + data[macsec_offset] = data[macsec_offset] | 0b1000_0000; - 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()), - ); + from_slice_assert_err( + &test, + &data, + SliceError::Macsec(err::macsec::HeaderError::UnexpectedVersion), + ); + } + }; + + // extensions + let extensions = [ + Ext::Macsec, + Ext::VlanTaggedFrame, + Ext::VlanDoubleTaggedFrame, + Ext::ProviderBridging, + ]; + + // none + from_x_slice_net_variants(base); + + // add up to three layers of extensions + for ext0 in extensions { + let test0 = ext0.add(base); + from_x_slice_net_variants(&test0); + test_macsec_mod(&test0); + len_errors(&test0); + content_errors(&test0); + + for ext1 in extensions { + let test1 = ext1.add(&test0); + from_x_slice_net_variants(&test1); + test_macsec_mod(&test1); + len_errors(&test1); + content_errors(&test1); + + for ext2 in extensions { + let test2 = ext2.add(&test1); + from_x_slice_net_variants(&test2); + test_macsec_mod(&test2); + len_errors(&test2); + content_errors(&test2); + + // above max supported link ext + for ext3 in extensions { + let mut test3 = test2.clone(); + let l = test3.link_exts.last_mut().unwrap(); + match l { + LinkExtHeader::Vlan(s) => { + s.ether_type = ext3.ether_type(); + } + LinkExtHeader::Macsec(m) => { + m.ptype = MacsecPType::Unmodified(ext3.ether_type()); + } + } + from_x_slice_assert_ok(&test3); } } } } } - fn from_x_slice_ip_variants(base: &TestPacket) { + fn from_x_slice_net_variants(base: &TestPacket) { // none from_x_slice_transport_variants(base); @@ -652,7 +943,7 @@ mod test { from_slice_assert_err( &test, &data[..base_len + len], - if test.link.is_some() || test.vlan.is_some() { + if test.link.is_some() || !test.link_exts.is_empty() { SliceError::Len(err.clone()) } else { SliceError::Len({ @@ -681,7 +972,7 @@ mod test { from_slice_assert_err( &test, &data, - if test.link.is_some() || test.vlan.is_some() { + if test.link.is_some() || !test.link_exts.is_empty() { SliceError::Ipv4( err::ipv4::HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 }, ) @@ -719,8 +1010,10 @@ mod test { 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), + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len + - auth.header_len(), ); let data = test.to_vec(&[]); @@ -779,38 +1072,38 @@ mod test { 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(); + for len in 0..ipv6.header_len() { + let mut test = test.clone(); + test.set_payload_len_link_ext(len); - let err = err::LenError { - required_len: ipv6.header_len(), - len, - len_source: LenSource::Slice, - layer: err::Layer::Ipv6Header, - layer_start_offset: base_len, - }; + let base_len = test.len(&[]) - ipv6.header_len(); + let data = test.to_vec(&[]); + 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() - } - }) - }, - ); - } + from_slice_assert_err( + &test, + &data[..base_len + len], + if test.link.is_some() || !test.link_exts.is_empty() { + 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 @@ -825,7 +1118,7 @@ mod test { from_slice_assert_err( &test, &data, - if test.link.is_some() || test.vlan.is_some() { + if test.link.is_some() || !test.link_exts.is_empty() { SliceError::Ipv6(err::ipv6::HeaderError::UnexpectedVersion { version_number: 0, }) @@ -870,8 +1163,10 @@ mod test { 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), + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len + - auth.header_len(), ); let data = test.to_vec(&[]); @@ -922,6 +1217,65 @@ mod test { } } } + + // arp + { + let arp = ArpPacket::new( + ArpHardwareId::ETHERNET, + EtherType::IPV4, + ArpOperation::REPLY, + &[0u8; 6], + &[0u8; 4], + &[0u8; 6], + &[0u8; 4], + ) + .unwrap(); + + let mut test = base.clone(); + test.set_ether_type(ether_type::ARP); + test.net = Some(NetHeaders::Arp(arp.clone())); + test.set_payload_len(0); + + // ok arp + from_x_slice_assert_ok(&test); + + // len error + { + for len in 0..arp.packet_len() { + let mut test = test.clone(); + test.set_payload_len_link_ext(len); + let base_len = test.len(&[]) - arp.packet_len(); + let data = test.to_vec(&[]); + + let err = err::LenError { + required_len: if len < 8 { 8 } else { arp.packet_len() }, + len, + len_source: if len < 8 { + LenSource::Slice + } else { + LenSource::ArpAddrLengths + }, + layer: Layer::Arp, + layer_start_offset: base_len, + }; + + from_slice_assert_err( + &test, + &data[..base_len + len], + SliceError::Len({ + if len < 8 { + let mut err = err.clone(); + err.required_len = 8; + err.layer = Layer::Arp; + err + } else { + err.clone() + } + }), + ); + } + } + } } fn from_x_slice_transport_variants(base: &TestPacket) { @@ -961,7 +1315,10 @@ mod test { let mut test = test.clone(); // set payload length - test.set_payload_le_from_ip_on(len as isize); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len, + ); // generate data let data = test.to_vec(&[]); @@ -1012,7 +1369,10 @@ mod test { 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); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len, + ); let data = test.to_vec(&[]); let base_len = test.len(&[]) - (tcp.header_len() as usize); @@ -1073,7 +1433,10 @@ mod test { 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); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len, + ); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv4.header_len(); @@ -1122,7 +1485,10 @@ mod test { 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); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len, + ); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv6.header_len(); @@ -1169,32 +1535,44 @@ mod test { 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.link_exts, test.link_exts); 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]); + if test.has_arp() { + assert_eq!(result.payload.slice(), &[]); + } else { + 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(); + if test.link.is_none() && !test.link_exts.is_empty() { + let ether_types: &[EtherType] = match test.link_exts.first().unwrap() { + LinkExtHeader::Vlan(_) => &VLAN_ETHER_TYPES, + LinkExtHeader::Macsec(_) => &MACSEC_ETHER_TYPES, + }; + for ether_type in 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.link_exts, test.link_exts); 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]); + if test.has_arp() { + assert_eq!(result.payload.slice(), &[]); + } else { + assert_eq!(result.payload.slice(), &[1, 2, 3, 4]); + } } } } - // from_ether_type (ip at start) - if test.link.is_none() && test.vlan.is_none() { + // from_ether_type (net at start) + if test.link.is_none() && test.link_exts.is_empty() { if let Some(ip) = &test.net { let result = PacketHeaders::from_ether_type( match ip { @@ -1206,21 +1584,28 @@ mod test { ) .unwrap(); assert_eq!(result.link, test.link); - assert_eq!(result.vlan, test.vlan); + assert_eq!(result.link_exts, test.link_exts); 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]); + if test.has_arp() { + assert_eq!(result.payload.slice(), &[]); + } else { + 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() { + if test.link.is_none() + && test.link_exts.is_empty() + && test.net.as_ref().map(|v| v.is_ip()).unwrap_or(false) + { let result = PacketHeaders::from_ip_slice(&data).unwrap(); assert_eq!(result.link, test.link); - assert_eq!(result.vlan, test.vlan); + assert_eq!(result.link_exts, test.link_exts); assert_eq!(result.net, test.net); if is_fragmented { assert_eq!(result.transport, None); @@ -1242,16 +1627,20 @@ mod test { ); } // from_ether_type (vlan at start) - if test.link.is_none() && test.vlan.is_some() { - for ether_type in VLAN_ETHER_TYPES { + if test.link.is_none() && !test.link_exts.is_empty() { + let ether_types: &[EtherType] = match test.link_exts.first().unwrap() { + LinkExtHeader::Vlan(_) => &VLAN_ETHER_TYPES, + LinkExtHeader::Macsec(_) => &MACSEC_ETHER_TYPES, + }; + for ether_type in ether_types { assert_eq!( err.clone(), - PacketHeaders::from_ether_type(ether_type, &data).unwrap_err() + 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 test.link.is_none() && test.link_exts.is_empty() { if let Some(ip) = &test.net { let err = PacketHeaders::from_ether_type( match ip { @@ -1266,7 +1655,10 @@ mod test { } } // from_ip_slice - if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { + if test.link.is_none() + && test.link_exts.is_empty() + && test.net.as_ref().map(|v| v.is_ip()).unwrap_or(false) + { assert_eq!(err, PacketHeaders::from_ip_slice(&data).unwrap_err()); } } diff --git a/etherparse/src/payload_slice.rs b/etherparse/src/payload_slice.rs index 07fa7022..bd9241ca 100644 --- a/etherparse/src/payload_slice.rs +++ b/etherparse/src/payload_slice.rs @@ -5,19 +5,28 @@ use crate::*; pub enum PayloadSlice<'a> { /// No specific payload (e.g. ARP packet). Empty, + /// Payload with it's type identified by an ether type number /// (e.g. after an ethernet II or vlan header). Ether(EtherPayloadSlice<'a>), + + /// MACsec encrypted or modified payload. + MacsecMod(&'a [u8]), + /// Payload with is's type identified by an ip number (e.g. /// after an IP header or after an) Ip(IpPayloadSlice<'a>), + /// UDP payload. Udp(&'a [u8]), + /// TCP payload. Tcp(&'a [u8]), + /// Payload part of an ICMP V4 message. Check [`crate::Icmpv4Type`] /// for a description what will be part of the payload. Icmpv4(&'a [u8]), + /// Payload part of an ICMP V4 message. Check [`crate::Icmpv6Type`] /// for a description what will be part of the payload. Icmpv6(&'a [u8]), @@ -28,6 +37,7 @@ impl<'a> PayloadSlice<'a> { match self { PayloadSlice::Empty => &[], PayloadSlice::Ether(s) => s.payload, + PayloadSlice::MacsecMod(s) => s, PayloadSlice::Ip(s) => s.payload, PayloadSlice::Udp(s) => s, PayloadSlice::Tcp(s) => s, @@ -83,6 +93,7 @@ mod test { assert_eq!( Ether(EtherPayloadSlice { ether_type: EtherType::IPV4, + len_source: LenSource::Slice, payload: &payload }) .slice(), diff --git a/etherparse/src/sliced_packet.rs b/etherparse/src/sliced_packet.rs index e9756020..86e8b9e0 100644 --- a/etherparse/src/sliced_packet.rs +++ b/etherparse/src/sliced_packet.rs @@ -1,3 +1,5 @@ +use arrayvec::ArrayVec; + use crate::*; /// Packet slice split into multiple slices containing the different headers & payload. @@ -36,25 +38,31 @@ use crate::*; /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); -/// println!("vlan: {:?}", value.vlan); -/// println!("net: {:?}", value.net); +/// println!("link_exts: {:?}", value.link_exts); // vlan & macsec +/// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } -/// } +/// }; /// ``` #[derive(Clone, Debug, Eq, PartialEq)] pub struct SlicedPacket<'a> { /// Ethernet II header if present. pub link: Option>, - /// Single or double vlan headers if present. - pub vlan: Option>, + + /// Link extensions (VLAN & MAC Sec headers). + pub link_exts: ArrayVec, { SlicedPacket::LINK_EXTS_CAP }>, + /// IPv4 or IPv6 header, IP extension headers & payload if present. pub net: Option>, + /// TCP or UDP header & payload if present. pub transport: Option>, } impl<'a> SlicedPacket<'a> { + /// Maximum supported number of link extensions. + pub const LINK_EXTS_CAP: usize = 3; + /// 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 @@ -84,14 +92,14 @@ impl<'a> SlicedPacket<'a> { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } - /// } + /// }; /// ``` pub fn from_ethernet(data: &'a [u8]) -> Result, err::packet::SliceError> { - SlicedPacketCursor::new(data).slice_ethernet2() + SlicedPacketCursor::new().slice_ethernet2(data) } /// Separates a network packet slice into different slices containing the @@ -126,14 +134,14 @@ impl<'a> SlicedPacket<'a> { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } - /// } + /// }; /// ``` pub fn from_linux_sll(data: &'a [u8]) -> Result, err::packet::SliceError> { - SlicedPacketCursor::new(data).slice_linux_sll() + SlicedPacketCursor::new().slice_linux_sll(data) } /// Separates a network packet slice into different slices containing the headers using @@ -183,29 +191,27 @@ impl<'a> SlicedPacket<'a> { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); - /// println!("vlan: {:?}", value.vlan); - /// println!("net: {:?}", value.net); + /// println!("link_exts: {:?}", value.link_exts); // vlan & macsec + /// println!("net: {:?}", value.net); // ip & arp /// println!("transport: {:?}", value.transport); /// } - /// } + /// }; /// ``` pub fn from_ether_type( ether_type: EtherType, data: &'a [u8], ) -> Result, err::packet::SliceError> { - use ether_type::*; - let mut cursor = SlicedPacketCursor::new(data); + let mut cursor = SlicedPacketCursor::new(); cursor.result.link = Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type, + len_source: LenSource::Slice, 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(), - ARP => cursor.slice_arp(), - _ => Ok(cursor.result), - } + cursor.slice_ether_type(EtherPayloadSlice { + ether_type, + len_source: LenSource::Slice, + payload: data, + }) } /// Separates a network packet slice into different slices containing the headers from the ip header downwards. @@ -236,16 +242,16 @@ impl<'a> SlicedPacket<'a> { /// Ok(value) => { /// //link & vlan fields are empty when parsing from ip downwards /// assert_eq!(None, value.link); - /// assert_eq!(None, value.vlan); + /// assert!(value.link_exts.is_empty()); /// /// //ip & transport (udp or tcp) /// println!("net: {:?}", value.net); /// println!("transport: {:?}", value.transport); /// } - /// } + /// }; /// ``` pub fn from_ip(data: &'a [u8]) -> Result, err::packet::SliceError> { - SlicedPacketCursor::new(data).slice_ip() + SlicedPacketCursor::new().slice_ip(data) } /// If the slice in the `payload` field contains an ethernet payload @@ -260,29 +266,23 @@ impl<'a> SlicedPacket<'a> { pub fn payload_ether_type(&self) -> Option { 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(last_ext) = &self.link_exts.last() { + use LinkExtSlice::*; + match last_ext { + Vlan(single_vlan_slice) => Some(single_vlan_slice.ether_type()), + Macsec(macsec_slice) => macsec_slice.next_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)), + LinuxSllProtocolType::EtherType(EtherType(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)), + LinuxSllProtocolType::EtherType(EtherType(v)) => Some(EtherType(v)), _ => None, }, } @@ -297,25 +297,45 @@ impl<'a> SlicedPacket<'a> { /// header is returned and if there is no VLAN header is present in the /// link field is returned. pub fn ether_payload(&self) -> Option> { - if let Some(vlan) = self.vlan.as_ref() { - match vlan { - VlanSlice::SingleVlan(s) => Some(s.payload()), - VlanSlice::DoubleVlan(s) => Some(s.payload()), + if let Some(last_ext) = self.link_exts.last() { + let mut len_source = LenSource::Slice; + for e in &self.link_exts { + match e { + LinkExtSlice::Vlan(_) => {} + LinkExtSlice::Macsec(m) => { + if m.header.short_len() != MacsecShortLen::ZERO { + len_source = LenSource::MacsecShortLength; + } + } + } + } + match last_ext { + LinkExtSlice::Vlan(v) => { + let mut p = v.payload(); + p.len_source = len_source; + Some(p) + } + LinkExtSlice::Macsec(m) => { + if let Some(mut p) = m.ether_payload() { + p.len_source = len_source; + Some(p) + } else { + None + } + } } } 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(_) => { + LinuxSllProtocolType::EtherType(_) => { 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(_) => { + LinuxSllProtocolType::EtherType(_) => { Some(EtherPayloadSlice::try_from(e.clone()).ok()?) } _ => None, @@ -350,11 +370,46 @@ impl<'a> SlicedPacket<'a> { Some(Arp(_)) | None => false, } } + + /// Returns the vlan headers present in the sliced packet. + pub fn vlan(&self) -> Option> { + let mut result = None; + for ext in &self.link_exts { + if let LinkExtSlice::Vlan(vlan_slice) = ext { + if let Some(outer) = result { + return Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer, + inner: vlan_slice.clone(), + })); + } else { + result = Some(vlan_slice.clone()); + } + } + } + result.map(VlanSlice::SingleVlan) + } + + /// Returns the VLAN ids present in this packet. + pub fn vlan_ids(&self) -> ArrayVec { + let mut result = ArrayVec::::new_const(); + for e in &self.link_exts { + // SAFETY: Safe as the vlan ids array has the same size as slice.link_exts. + if let LinkExtSlice::Vlan(s) = e { + unsafe { + result.push_unchecked(s.vlan_identifier()); + } + } + } + result + } } #[cfg(test)] mod test { + use std::vec::Vec; + use super::*; + use crate::err::macsec; use crate::err::{packet::SliceError, Layer, LenError}; use crate::test_gens::*; use crate::test_packet::TestPacket; @@ -365,12 +420,13 @@ mod test { ether_type::PROVIDER_BRIDGING, ether_type::VLAN_DOUBLE_TAGGED_FRAME, ]; + const MACSEC_ETHER_TYPES: [EtherType; 1] = [ether_type::MACSEC]; #[test] fn clone_eq() { let header = SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; @@ -382,15 +438,15 @@ mod test { use alloc::format; let header = SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; assert_eq!( format!("{:?}", header), format!( - "SlicedPacket {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?} }}", - header.link, header.vlan, header.net, header.transport, + "SlicedPacket {{ link: {:?}, link_exts: {:?}, net: {:?}, transport: {:?} }}", + header.link, header.link_exts, header.net, header.transport, ) ); } @@ -403,7 +459,7 @@ mod test { assert_eq!( SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, } @@ -427,6 +483,7 @@ mod test { SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(), Some(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -439,15 +496,17 @@ mod test { SlicedPacket { link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload })), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, } .ether_payload(), Some(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -472,10 +531,85 @@ mod test { SlicedPacket::from_linux_sll(&buf).unwrap().ether_payload(), Some(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); } + // only linux sll + { + let test = [ + (None, ArpHardwareId::FRAD, LinuxSllProtocolType::Ignored(0)), + ( + None, + ArpHardwareId::NETLINK, + LinuxSllProtocolType::NetlinkProtocolType(0), + ), + ( + None, + ArpHardwareId::IPGRE, + LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(0), + ), + ( + Some(ether_type::WAKE_ON_LAN), + ArpHardwareId::ETHERNET, + LinuxSllProtocolType::EtherType(ether_type::WAKE_ON_LAN), + ), + ( + None, + ArpHardwareId::ETHERNET, + LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType::CAN), + ), + ]; + + for (expected, arp_hrd_type, protocol_type) in test { + { + let l = LinuxSllHeader { + packet_type: LinuxSllPacketType::HOST, + arp_hrd_type, + sender_address_valid_length: 6, + sender_address: [0; 8], + protocol_type, + }; + + let mut bytes = Vec::with_capacity(l.header_len()); + l.write(&mut bytes).unwrap(); + + let s = SlicedPacket::from_linux_sll(&bytes).unwrap(); + assert_eq!( + expected.map(|ether_type| { + EtherPayloadSlice { + ether_type, + len_source: LenSource::Slice, + payload: &[], + } + }), + s.ether_payload() + ); + } + { + let s = SlicedPacket { + link: Some(LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice { + protocol_type: protocol_type, + payload: &[], + })), + link_exts: Default::default(), + net: None, + transport: None, + }; + assert_eq!( + expected.map(|ether_type| { + EtherPayloadSlice { + ether_type, + len_source: LenSource::Slice, + payload: &[], + } + }), + s.ether_payload() + ); + } + } + } // ether type payload { @@ -486,13 +620,14 @@ mod test { protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN), payload: &payload })), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, } .ether_payload(), Some(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -521,6 +656,7 @@ mod test { SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(), Some(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); @@ -556,10 +692,80 @@ mod test { SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(), Some(EtherPayloadSlice { ether_type: EtherType::WAKE_ON_LAN, + len_source: LenSource::Slice, payload: &payload }) ); } + + // macsec + { + let tests = [ + ( + Some(ether_type::WAKE_ON_LAN), + MacsecPType::Unmodified(ether_type::WAKE_ON_LAN), + ), + (None, MacsecPType::Modified), + (None, MacsecPType::Encrypted), + (None, MacsecPType::EncryptedUnmodified), + ]; + for (expected, ptype) in tests { + let eth_mod = Ethernet2Header { + source: [0; 6], + destination: [0; 6], + ether_type: ether_type::VLAN_TAGGED_FRAME, + }; + let vlan = SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::MACSEC, + }; + let macsec0 = MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType::MACSEC), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }; + let mut macsec1 = MacsecHeader { + ptype, + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }; + let payload = [1, 2, 3, 4]; + macsec1.set_payload_len(payload.len()); + let mut serialized = Vec::with_capacity( + eth_mod.header_len() + + vlan.header_len() + + macsec0.header_len() + + macsec1.header_len() + + payload.len(), + ); + eth_mod.write(&mut serialized).unwrap(); + vlan.write(&mut serialized).unwrap(); + macsec0.write(&mut serialized).unwrap(); + macsec1.write(&mut serialized).unwrap(); + serialized.extend_from_slice(&payload); + + assert_eq!( + expected.map(|ether_type| EtherPayloadSlice { + ether_type, + len_source: LenSource::MacsecShortLength, + payload: &payload, + }), + SlicedPacket::from_ethernet(&serialized) + .unwrap() + .ether_payload() + ); + } + } } #[test] @@ -570,7 +776,7 @@ mod test { assert_eq!( SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, } @@ -662,7 +868,7 @@ mod test { assert_eq!( SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, } @@ -789,12 +995,218 @@ mod test { } } + #[test] + fn vlan_vlan_ids() { + // no content + assert_eq!( + SlicedPacket { + link: None, + link_exts: ArrayVec::new_const(), + net: None, + transport: None, + } + .vlan(), + None + ); + assert_eq!( + SlicedPacket { + link: None, + link_exts: ArrayVec::new_const(), + net: None, + transport: None, + } + .vlan_ids(), + ArrayVec::::new_const() + ); + + // single vlan header + { + let payload = [1, 2, 3, 4]; + let mut buf = Vec::with_capacity(SingleVlanHeader::LEN + 4); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = SlicedPacket::from_ether_type(ether_type::VLAN_TAGGED_FRAME, &buf).unwrap(); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::SingleVlan(SingleVlanSlice { slice: &buf[..] })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids + }); + } + + // two vlan header + { + let payload = [1, 2, 3, 4]; + let mut buf = Vec::with_capacity(SingleVlanHeader::LEN * 2 + 4); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = + SlicedPacket::from_ether_type(ether_type::VLAN_DOUBLE_TAGGED_FRAME, &buf).unwrap(); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer: SingleVlanSlice { slice: &buf }, + inner: SingleVlanSlice { + slice: &buf[SingleVlanHeader::LEN..] + }, + })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // two vlan header & macsec header + { + let payload = [1, 2, 3, 4]; + let macsec = MacsecHeader { + ptype: MacsecPType::Unmodified(ether_type::VLAN_DOUBLE_TAGGED_FRAME), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }; + let mut buf = Vec::with_capacity(macsec.header_len() + SingleVlanHeader::LEN * 2 + 4); + buf.extend_from_slice(&macsec.to_bytes()); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = SlicedPacket::from_ether_type(ether_type::MACSEC, &buf).unwrap(); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer: SingleVlanSlice { + slice: &buf[macsec.header_len()..] + }, + inner: SingleVlanSlice { + slice: &buf[macsec.header_len() + SingleVlanHeader::LEN..] + }, + })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids + }); + } + + // three vlan header + { + let payload = [1, 2, 3, 4]; + let mut buf = Vec::with_capacity(SingleVlanHeader::LEN * 3 + 4); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(2).unwrap(), + ether_type: EtherType::VLAN_TAGGED_FRAME, + } + .to_bytes(), + ); + buf.extend_from_slice( + &SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(3).unwrap(), + ether_type: EtherType::WAKE_ON_LAN, + } + .to_bytes(), + ); + buf.extend_from_slice(&payload); + + let slice = + SlicedPacket::from_ether_type(ether_type::VLAN_DOUBLE_TAGGED_FRAME, &buf).unwrap(); + + assert_eq!( + slice.vlan(), + Some(VlanSlice::DoubleVlan(DoubleVlanSlice { + outer: SingleVlanSlice { slice: &buf }, + inner: SingleVlanSlice { + slice: &buf[SingleVlanHeader::LEN..] + }, + })) + ); + assert_eq!(slice.vlan_ids(), { + let mut ids = ArrayVec::::new_const(); + ids.push(VlanId::try_new(1).unwrap()); + ids.push(VlanId::try_new(2).unwrap()); + ids.push(VlanId::try_new(3).unwrap()); + ids + }); + } + } + #[test] fn from_x_slice() { // no eth - from_x_slice_vlan_variants(&TestPacket { + from_x_slice_link_exts_variants(&TestPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }); @@ -808,9 +1220,10 @@ mod test { SlicedPacket { link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: EtherType(0x8221), + len_source: LenSource::Slice, payload: &data })), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None } @@ -826,13 +1239,13 @@ mod test { }; let test = TestPacket { link: Some(LinkHeader::Ethernet2(eth.clone())), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; // ok ethernet header (with unknown next) - from_x_slice_vlan_variants(&test); + from_x_slice_link_exts_variants(&test); // eth len error { @@ -862,7 +1275,7 @@ mod test { }; let test = TestPacket { link: Some(LinkHeader::LinuxSll(linux_sll.clone())), - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; @@ -885,99 +1298,175 @@ mod test { } } - fn from_x_slice_vlan_variants(base: &TestPacket) { - // none - from_x_slice_ip_variants(base); + fn from_x_slice_link_exts_variants(base: &TestPacket) { + #[derive(Copy, Clone, Eq, PartialEq)] + enum Ext { + Macsec, + VlanTaggedFrame, + VlanDoubleTaggedFrame, + ProviderBridging, + } - // 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(), - }; + impl Ext { + pub fn ether_type(&self) -> EtherType { + match self { + Ext::Macsec => EtherType::MACSEC, + Ext::VlanTaggedFrame => EtherType::VLAN_TAGGED_FRAME, + Ext::VlanDoubleTaggedFrame => EtherType::VLAN_DOUBLE_TAGGED_FRAME, + Ext::ProviderBridging => EtherType::PROVIDER_BRIDGING, + } + } - for vlan_ether_type in VLAN_ETHER_TYPES { + pub fn add(&self, base: &TestPacket) -> TestPacket { let mut test = base.clone(); - test.set_ether_type(vlan_ether_type); - test.vlan = Some(VlanHeader::Single(single.clone())); + test.set_ether_type(self.ether_type()); + test.link_exts + .try_push(match self { + Ext::Macsec => LinkExtHeader::Macsec(MacsecHeader { + ptype: MacsecPType::Unmodified(EtherType(3)), + endstation_id: false, + scb: false, + an: MacsecAn::ZERO, + short_len: MacsecShortLen::ZERO, + packet_nr: 0, + sci: None, + }), + Ext::VlanTaggedFrame + | Ext::VlanDoubleTaggedFrame + | Ext::ProviderBridging => LinkExtHeader::Vlan(SingleVlanHeader { + pcp: VlanPcp::ZERO, + drop_eligible_indicator: false, + vlan_id: VlanId::try_new(1).unwrap(), + ether_type: 3.into(), + }), + }) + .unwrap(); + test + } + } - // ok vlan header - from_x_slice_ip_variants(&test); + let test_macsec_mod = |test: &TestPacket| { + for ptype in [ + MacsecPType::Modified, + MacsecPType::Encrypted, + MacsecPType::EncryptedUnmodified, + ] { + let mut test = test.clone(); + if let Some(LinkExtHeader::Macsec(m)) = test.link_exts.last_mut() { + m.ptype = ptype; + } + if matches!(test.link_exts.last(), Some(LinkExtHeader::Macsec(_))) { + from_x_slice_assert_ok(&test); + } + } + }; - // len error - { - let data = test.to_vec(&[]); - for len in 0..single.header_len() { - let base_len = test.len(&[]) - single.header_len(); + let len_errors = |test: &TestPacket| { + let data = test.to_vec(&[]); + let req_len = test.link_exts.last().unwrap().header_len(); + for len in 0..req_len { + let base_len = test.len(&[]) - req_len; + + let (err_req_len, err_layer) = match test.link_exts.last().unwrap() { + LinkExtHeader::Vlan(h) => (h.header_len(), Layer::VlanHeader), + LinkExtHeader::Macsec(_) => { + if len < 6 { + (6, Layer::MacsecHeader) + } else { + (req_len, Layer::MacsecHeader) + } + } + }; - 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()), - ); + let mut len_source = LenSource::Slice; + for prev_exts in test.link_exts.iter().rev().skip(1) { + if let LinkExtHeader::Macsec(m) = prev_exts { + if m.short_len != MacsecShortLen::ZERO { + len_source = LenSource::MacsecShortLength; + } } } - } - } - // 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 err = LenError { + required_len: err_req_len, + len, + len_source, + layer: err_layer, + layer_start_offset: base_len, }; - let mut test = base.clone(); - test.set_ether_type(outer_vlan_ether_type); - test.vlan = Some(VlanHeader::Double(double.clone())); + from_slice_assert_err(&test, &data[..base_len + len], SliceError::Len(err.clone())); + } + }; - // ok double vlan header - from_x_slice_ip_variants(&test); + let content_errors = |test: &TestPacket| { + if let Some(LinkExtHeader::Macsec(last)) = test.link_exts.last() { + let mut data = test.to_vec(&[]); - // len error - { - let data = test.to_vec(&[]); - for len in 0..SingleVlanHeader::LEN { - let base_len = test.len(&[]) - SingleVlanHeader::LEN; + // inject bad version id + let macsec_offset = data.len() - last.header_len(); + data[macsec_offset] = data[macsec_offset] | 0b1000_0000; - 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()), - ); + from_slice_assert_err( + &test, + &data, + SliceError::Macsec(macsec::HeaderError::UnexpectedVersion), + ); + } + }; + + // extensions + let extensions = [ + Ext::Macsec, + Ext::VlanTaggedFrame, + Ext::VlanDoubleTaggedFrame, + Ext::ProviderBridging, + ]; + + // none + from_x_slice_net_variants(base); + + // add up to three layers of extensions + for ext0 in extensions { + let test0 = ext0.add(base); + from_x_slice_net_variants(&test0); + test_macsec_mod(&test0); + len_errors(&test0); + content_errors(&test0); + + for ext1 in extensions { + let test1 = ext1.add(&test0); + from_x_slice_net_variants(&test1); + test_macsec_mod(&test1); + len_errors(&test1); + content_errors(&test1); + + for ext2 in extensions { + let test2 = ext2.add(&test1); + from_x_slice_net_variants(&test2); + test_macsec_mod(&test2); + len_errors(&test2); + content_errors(&test2); + + // above max supported link ext + for ext3 in extensions { + let mut test3 = test2.clone(); + let l = test3.link_exts.last_mut().unwrap(); + match l { + LinkExtHeader::Vlan(s) => { + s.ether_type = ext3.ether_type(); + } + LinkExtHeader::Macsec(m) => { + m.ptype = MacsecPType::Unmodified(ext3.ether_type()); + } + } + from_x_slice_assert_ok(&test3); } } } } } - fn from_x_slice_ip_variants(base: &TestPacket) { + fn from_x_slice_net_variants(base: &TestPacket) { // none from_x_slice_transport_variants(base); @@ -991,19 +1480,25 @@ mod test { }; { - 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); + let test = { + 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); + test + }; // ok ipv4 from_x_slice_transport_variants(&test); // ipv4 len error { - let data = test.to_vec(&[]); for len in 0..ipv4.header_len() { + let mut test = test.clone(); let base_len = test.len(&[]) - ipv4.header_len(); + test.set_payload_len_link_ext(len); + + let data = test.to_vec(&[]); let err = LenError { required_len: ipv4.header_len(), @@ -1015,7 +1510,7 @@ mod test { from_slice_assert_err( &test, &data[..base_len + len], - if test.link.is_some() || test.vlan.is_some() { + if test.link.is_some() || !test.link_exts.is_empty() { SliceError::Len(err.clone()) } else { SliceError::Len({ @@ -1046,7 +1541,7 @@ mod test { from_slice_assert_err( &test, &data, - if test.link.is_some() || test.vlan.is_some() { + if test.link.is_some() || !test.link_exts.is_empty() { SliceError::Ipv4( err::ipv4::HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 }, ) @@ -1072,7 +1567,12 @@ mod test { 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) + + test + .link_exts + .as_ref() + .iter() + .map(|h| h.header_len()) + .sum::() }, }; @@ -1105,9 +1605,11 @@ mod test { 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), + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len + - auth.header_len(), ); + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); let data = test.to_vec(&[]); let base_len = test.len(&[]) - auth.header_len(); @@ -1156,47 +1658,51 @@ mod test { // 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); + let test = { + 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); + test + }; // 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(); + 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, - }; + let mut test = test.clone(); + test.set_payload_len_link_ext(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() - } - }) - }, - ); - } + let data = test.to_vec(&[]); + 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.link_exts.is_empty() { + 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 @@ -1212,7 +1718,7 @@ mod test { from_slice_assert_err( &test, &data, - if test.link.is_some() || test.vlan.is_some() { + if test.link.is_some() || !test.link_exts.is_empty() { SliceError::Ipv6(err::ipv6::HeaderError::UnexpectedVersion { version_number: 0, }) @@ -1257,9 +1763,11 @@ mod test { 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), + test.set_payload_len_link_ext( + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + len + - auth.header_len(), ); + test.set_payload_len_ip(-1 * (auth.header_len() as isize) + (len as isize)); let data = test.to_vec(&[]); let base_len = test.len(&[]) - auth.header_len(); @@ -1348,7 +1856,10 @@ mod test { let mut test = test.clone(); // set payload length - test.set_payload_le_from_ip_on(len as isize); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + len + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0), + ); // generate data let data = test.to_vec(&[]); @@ -1400,7 +1911,10 @@ mod test { 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); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + len + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0), + ); let data = test.to_vec(&[]); let base_len = test.len(&[]) - (tcp.header_len() as usize); @@ -1463,7 +1977,10 @@ mod test { 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); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + len + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0), + ); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv4.header_len(); @@ -1513,7 +2030,10 @@ mod test { 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); + test.set_payload_len_ip(len as isize); + test.set_payload_len_link_ext( + len + test.net.as_ref().map(|v| v.header_len()).unwrap_or(0), + ); let data = test.to_vec(&[]); let base_len = test.len(&[]) - icmpv6.header_len(); @@ -1558,7 +2078,15 @@ mod test { None => None, } ); - assert_eq!(test.vlan, result.vlan.as_ref().map(|e| e.to_header())); + assert_eq!( + test.link_exts, + result + .link_exts + .as_ref() + .iter() + .map(|e| e.to_header()) + .collect::>() + ); assert_eq!( test.net, result.net.as_ref().map(|s: &NetSlice| -> NetHeaders { @@ -1628,13 +2156,18 @@ mod test { 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(); + if test.link.is_none() && !test.link_exts.is_empty() { + let ether_types: &[EtherType] = match test.link_exts.first().unwrap() { + LinkExtHeader::Vlan(_) => &VLAN_ETHER_TYPES, + LinkExtHeader::Macsec(_) => &MACSEC_ETHER_TYPES, + }; + for ether_type in ether_types { + let result = SlicedPacket::from_ether_type(*ether_type, &data).unwrap(); assert_eq!( result.link, Some(LinkSlice::EtherPayload(EtherPayloadSlice { - ether_type, + ether_type: *ether_type, + len_source: LenSource::Slice, payload: &data })) ); @@ -1642,7 +2175,7 @@ mod test { } } // from_ether_type (ip at start) - if test.link.is_none() && test.vlan.is_none() { + if test.link.is_none() && test.link_exts.is_empty() { if let Some(ip) = &test.net { let ether_type = match ip { NetHeaders::Ipv4(_, _) => ether_type::IPV4, @@ -1654,6 +2187,7 @@ mod test { result.link, Some(LinkSlice::EtherPayload(EtherPayloadSlice { ether_type, + len_source: LenSource::Slice, payload: &data })) ); @@ -1661,7 +2195,7 @@ mod test { } } // from_ip_slice - if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { + if test.link.is_none() && test.link_exts.is_empty() && test.net.is_some() { let result = SlicedPacket::from_ip(&data).unwrap(); assert_test_result(&test, &payload, &result); } @@ -1683,16 +2217,20 @@ mod test { } } // from_ether_type (vlan at start) - if test.link.is_none() && test.vlan.is_some() { - for ether_type in VLAN_ETHER_TYPES { + if test.link.is_none() && !test.link_exts.is_empty() { + let ether_types: &[EtherType] = match test.link_exts.first().unwrap() { + LinkExtHeader::Vlan(_) => &VLAN_ETHER_TYPES, + LinkExtHeader::Macsec(_) => &MACSEC_ETHER_TYPES, + }; + for ether_type in ether_types { assert_eq!( err.clone(), - SlicedPacket::from_ether_type(ether_type, &data).unwrap_err() + 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 test.link.is_none() && test.link_exts.is_empty() { if let Some(ip) = &test.net { let err = SlicedPacket::from_ether_type( match ip { @@ -1707,7 +2245,7 @@ mod test { } } // from_ip_slice - if test.link.is_none() && test.vlan.is_none() && test.net.is_some() { + if test.link.is_none() && test.link_exts.is_empty() && test.net.is_some() { assert_eq!(err, SlicedPacket::from_ip(&data).unwrap_err()); } } @@ -1716,9 +2254,11 @@ mod test { #[test] fn payload_ether_type( ref eth in ethernet_2_unknown(), + ether_type in ether_type_unknown(), ref linux_sll in linux_sll_any(), ref vlan_outer in vlan_single_unknown(), ref vlan_inner in vlan_single_unknown(), + ref macsec in macsec_any(), ref ipv4 in ipv4_unknown(), ref udp in udp_any(), ) { @@ -1729,7 +2269,7 @@ mod test { { let s = SlicedPacket{ link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }; @@ -1738,17 +2278,44 @@ mod test { // 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() - ); + let test = [ + (None, ArpHardwareId::FRAD, LinuxSllProtocolType::Ignored(0)), + (None, ArpHardwareId::NETLINK, LinuxSllProtocolType::NetlinkProtocolType(0)), + (None, ArpHardwareId::IPGRE, LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(0)), + (Some(ether_type::WAKE_ON_LAN), ArpHardwareId::ETHERNET, LinuxSllProtocolType::EtherType(ether_type::WAKE_ON_LAN)), + (None, ArpHardwareId::ETHERNET, LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType::CAN)), + ]; + + for (expected, arp_hrd_type, protocol_type) in test { + { + let mut l = linux_sll.clone(); + l.arp_hrd_type = arp_hrd_type; + l.protocol_type = protocol_type; + + let mut bytes = Vec::with_capacity(linux_sll.header_len()); + l.write(&mut bytes).unwrap(); + + let s = SlicedPacket::from_linux_sll(&bytes).unwrap(); + assert_eq!( + expected, + s.payload_ether_type() + ); + } + { + let s = SlicedPacket{ + link: Some(LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice{ + protocol_type: protocol_type, + payload: &[] + })), + link_exts: Default::default(), + net: None, + transport: None, + }; + assert_eq!( + expected, + s.payload_ether_type() + ); + } } } @@ -1764,6 +2331,24 @@ mod test { ); } + // only ethernet payload + { + let s = SlicedPacket { + link: Some(LinkSlice::EtherPayload(EtherPayloadSlice { + ether_type, + len_source: LenSource::Slice, + payload: &[], + })), + link_exts: Default::default(), + net: None, + transport: None, + }; + assert_eq!( + Some(ether_type), + s.payload_ether_type() + ); + } + // with single vlan { let mut eth_mod = eth.clone(); @@ -1807,6 +2392,36 @@ mod test { ); } + // macsec + { + let tests = [ + (Some(ether_type), MacsecPType::Unmodified(ether_type)), + (None, MacsecPType::Modified), + (None, MacsecPType::Encrypted), + (None, MacsecPType::EncryptedUnmodified), + ]; + for (expected, ptype) in tests { + let mut eth_mod = eth.clone(); + eth_mod.ether_type = ether_type::MACSEC; + + let mut serialized = Vec::with_capacity( + eth_mod.header_len() + + macsec.header_len() + ); + eth_mod.write(&mut serialized).unwrap(); + let mut macsec = macsec.clone(); + macsec.ptype = ptype; + macsec.set_payload_len(0); + macsec.write(&mut serialized).unwrap(); + assert_eq!( + expected, + SlicedPacket::from_ethernet(&serialized) + .unwrap() + .payload_ether_type() + ); + } + } + // with ip { let builder = PacketBuilder::ethernet2(eth.source, eth.destination) diff --git a/etherparse/src/sliced_packet_cursor.rs b/etherparse/src/sliced_packet_cursor.rs index b6db3787..faa1775d 100644 --- a/etherparse/src/sliced_packet_cursor.rs +++ b/etherparse/src/sliced_packet_cursor.rs @@ -1,65 +1,55 @@ use crate::*; +use arrayvec::ArrayVec; /// Helper class for slicing packets pub(crate) struct SlicedPacketCursor<'a> { - pub slice: &'a [u8], pub offset: usize, pub len_source: LenSource, pub result: SlicedPacket<'a>, } impl<'a> SlicedPacketCursor<'a> { - pub fn new(slice: &'a [u8]) -> SlicedPacketCursor<'a> { + pub fn new() -> SlicedPacketCursor<'a> { SlicedPacketCursor { - slice, offset: 0, len_source: LenSource::Slice, result: SlicedPacket { link: None, - vlan: None, + link_exts: ArrayVec::new_const(), net: None, transport: None, }, } } - fn move_by(&mut self, len: usize) { - unsafe { - use core::slice::from_raw_parts; - self.slice = from_raw_parts(self.slice.as_ptr().add(len), self.slice.len() - len); - } - self.offset += len; - } - - pub fn slice_ethernet2(mut self) -> Result, err::packet::SliceError> { + pub fn slice_ethernet2( + mut self, + slice: &'a [u8], + ) -> Result, err::packet::SliceError> { use err::packet::SliceError::*; - use ether_type::*; use LinkSlice::*; - let result = Ethernet2Slice::from_slice_without_fcs(self.slice) + let result = Ethernet2Slice::from_slice_without_fcs(slice) .map_err(|err| Len(err.add_offset(self.offset)))?; //cache the ether_type for later - let ether_type = result.ether_type(); + let ether_payload = result.payload(); //set the new data - self.move_by(result.header_len()); + self.offset += result.header_len(); self.result.link = Some(Ethernet2(result)); //continue parsing (if required) - match ether_type { - ARP => self.slice_arp(), - IPV4 => self.slice_ipv4(), - IPV6 => self.slice_ipv6(), - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => self.slice_vlan(), - _ => Ok(self.result), - } + self.slice_ether_type(ether_payload) } - pub fn slice_linux_sll(mut self) -> Result, err::packet::SliceError> { + pub fn slice_linux_sll( + mut self, + slice: &'a [u8], + ) -> Result, err::packet::SliceError> { use err::packet::SliceError::*; - let result = LinuxSllSlice::from_slice(self.slice).map_err(|err| match err { + let result = LinuxSllSlice::from_slice(slice).map_err(|err| match err { err::linux_sll::HeaderSliceError::Len(len) => Len(len.add_offset(self.offset)), err::linux_sll::HeaderSliceError::Content(content) => { err::packet::SliceError::LinuxSll(content) @@ -67,66 +57,103 @@ impl<'a> SlicedPacketCursor<'a> { })?; //cache the protocol type for later - let protocol_type = result.protocol_type(); + let payload = result.payload(); //set the new data - self.move_by(result.header_len()); + self.offset += result.header_len(); self.result.link = Some(LinkSlice::LinuxSll(result)); //continue parsing (if required) - match protocol_type { - LinuxSllProtocolType::EtherType(EtherType::ARP) => self.slice_arp(), - LinuxSllProtocolType::EtherType(EtherType::IPV4) => self.slice_ipv4(), - LinuxSllProtocolType::EtherType(EtherType::IPV6) => self.slice_ipv6(), + match payload.protocol_type { + LinuxSllProtocolType::EtherType(next_ether_type) => { + self.slice_ether_type(EtherPayloadSlice { + ether_type: next_ether_type, + len_source: LenSource::Slice, + payload: payload.payload, + }) + } _ => Ok(self.result), } } - pub fn slice_vlan(mut self) -> Result, err::packet::SliceError> { + pub fn slice_ether_type( + mut self, + mut ether_payload: EtherPayloadSlice<'a>, + ) -> Result, err::packet::SliceError> { use err::packet::SliceError::*; use ether_type::*; - use VlanSlice::*; - // cache the starting slice so the later combining - // of outer & inner vlan is defined behavior (for miri) - let outer_start_slice = self.slice; - let outer = SingleVlanSlice::from_slice(self.slice) - .map_err(|err| Len(err.add_offset(self.offset)))?; - self.result.vlan = Some(SingleVlan(outer.clone())); - self.move_by(outer.header_len()); - - //check if it is a double vlan header - match outer.ether_type() { - //in case of a double vlan header continue with the inner - VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { - let inner = SingleVlanSlice::from_slice(self.slice) - .map_err(|err| Len(err.add_offset(self.offset)))?; - self.move_by(inner.header_len()); - - let inner_ether_type = inner.ether_type(); - self.result.vlan = Some(DoubleVlan(DoubleVlanSlice { - slice: outer_start_slice, - })); - - match inner_ether_type { - ARP => self.slice_arp(), - IPV4 => self.slice_ipv4(), - IPV6 => self.slice_ipv6(), - _ => Ok(self.result), + loop { + match ether_payload.ether_type { + VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { + if self.result.link_exts.is_full() { + return Ok(self.result); + } + + let vlan = SingleVlanSlice::from_slice(ether_payload.payload) + .map_err(|err| Len(err.add_offset(self.offset)))?; + self.offset += vlan.header_len(); + let vlan_payload = vlan.payload(); + // SAFETY: Safe, as the outer if verifies that there is still space in link_exts. + unsafe { + self.result + .link_exts + .push_unchecked(LinkExtSlice::Vlan(vlan)); + } + ether_payload = vlan_payload; } + MACSEC => { + if self.result.link_exts.is_full() { + return Ok(self.result); + } + + let macsec = MacsecSlice::from_slice(ether_payload.payload).map_err(|err| { + use err::macsec::HeaderSliceError as I; + use err::packet::SliceError as O; + match err { + I::Len(l) => O::Len(l.add_offset(self.offset)), + I::Content(h) => O::Macsec(h), + } + })?; + + // set offset & len source + self.offset += macsec.header.header_len(); + if macsec.header.short_len().value() > 0 { + self.len_source = LenSource::MacsecShortLength; + } + + let macsec_payload = macsec.payload.clone(); + + // SAFETY: Safe, as the outer if verifies that there is still space in link_exts. + unsafe { + self.result + .link_exts + .push_unchecked(LinkExtSlice::Macsec(macsec)); + } + + // only continue if the payload is unencrypted + if let MacsecPayloadSlice::Unmodified(e) = macsec_payload { + ether_payload = e; + } else { + return Ok(self.result); + } + } + ARP => return self.slice_arp(ether_payload.payload), + IPV4 => return self.slice_ipv4(ether_payload.payload), + IPV6 => return self.slice_ipv6(ether_payload.payload), + _ => return Ok(self.result), } - ARP => self.slice_arp(), - IPV4 => self.slice_ipv4(), - IPV6 => self.slice_ipv6(), - _ => Ok(self.result), } } - pub fn slice_ip(mut self) -> Result, err::packet::SliceError> { + pub fn slice_ip( + mut self, + slice: &'a [u8], + ) -> Result, err::packet::SliceError> { use err::packet::SliceError::*; // slice header, extension headers and identify payload range - let ip = IpSlice::from_slice(self.slice).map_err(|err| { + let ip = IpSlice::from_slice(slice).map_err(|err| { use err::ip::SliceError as I; match err { I::Len(mut err) => { @@ -149,10 +176,9 @@ impl<'a> SlicedPacketCursor<'a> { // SAFETY: The payload is a subslice of self.slice. // therefor calculating the offset from it is safe and // the result should always be a positive number. - payload.payload.as_ptr().offset_from(self.slice.as_ptr()) as usize + payload.payload.as_ptr().offset_from(slice.as_ptr()) as usize }; self.len_source = payload.len_source; - self.slice = payload.payload; self.result.net = Some(ip.into()); // continue to the lower layers @@ -160,26 +186,29 @@ impl<'a> SlicedPacketCursor<'a> { Ok(self.result) } else { match payload.ip_number { - ip_number::ICMP => self.slice_icmp4().map_err(Len), - ip_number::UDP => self.slice_udp().map_err(Len), - ip_number::TCP => self.slice_tcp().map_err(|err| { + ip_number::ICMP => self.slice_icmp4(payload.payload).map_err(Len), + ip_number::UDP => self.slice_udp(payload.payload).map_err(Len), + ip_number::TCP => self.slice_tcp(payload.payload).map_err(|err| { use err::tcp::HeaderSliceError as I; match err { I::Len(err) => Len(err), I::Content(err) => Tcp(err), } }), - ip_number::IPV6_ICMP => self.slice_icmp6().map_err(Len), + ip_number::IPV6_ICMP => self.slice_icmp6(payload.payload).map_err(Len), _ => Ok(self.result), } } } - pub fn slice_ipv4(mut self) -> Result, err::packet::SliceError> { + pub fn slice_ipv4( + mut self, + slice: &'a [u8], + ) -> Result, err::packet::SliceError> { use err::packet::SliceError::*; // slice ipv4 header & extension headers - let ipv4 = Ipv4Slice::from_slice(self.slice).map_err(|err| { + let ipv4 = Ipv4Slice::from_slice(slice).map_err(|err| { use err::ipv4::SliceError as I; match err { I::Len(mut err) => { @@ -199,35 +228,37 @@ impl<'a> SlicedPacketCursor<'a> { // SAFETY: The payload is a subslice of self.slice. // therefor calculating the offset from it is safe and // the result should always be a positive number. - payload.payload.as_ptr().offset_from(self.slice.as_ptr()) as usize + payload.payload.as_ptr().offset_from(slice.as_ptr()) as usize }; self.len_source = payload.len_source; - self.slice = payload.payload; self.result.net = Some(NetSlice::Ipv4(ipv4)); if payload.fragmented { Ok(self.result) } else { match payload.ip_number { - ip_number::UDP => self.slice_udp().map_err(Len), - ip_number::TCP => self.slice_tcp().map_err(|err| { + ip_number::UDP => self.slice_udp(payload.payload).map_err(Len), + ip_number::TCP => self.slice_tcp(payload.payload).map_err(|err| { use err::tcp::HeaderSliceError as I; match err { I::Len(err) => Len(err), I::Content(err) => Tcp(err), } }), - ip_number::ICMP => self.slice_icmp4().map_err(Len), - ip_number::IPV6_ICMP => self.slice_icmp6().map_err(Len), + ip_number::ICMP => self.slice_icmp4(payload.payload).map_err(Len), + ip_number::IPV6_ICMP => self.slice_icmp6(payload.payload).map_err(Len), _ => Ok(self.result), } } } - pub fn slice_ipv6(mut self) -> Result, err::packet::SliceError> { + pub fn slice_ipv6( + mut self, + slice: &'a [u8], + ) -> Result, err::packet::SliceError> { use err::packet::SliceError::*; - let ipv6 = Ipv6Slice::from_slice(self.slice).map_err(|err| { + let ipv6 = Ipv6Slice::from_slice(slice).map_err(|err| { use err::ipv6::SliceError as I; match err { I::Len(mut err) => { @@ -247,13 +278,9 @@ impl<'a> SlicedPacketCursor<'a> { // SAFETY: The payload is a subslice of self.slice. // therefor calculating the offset from it is safe and // the result should always be a positive number. - ipv6.payload() - .payload - .as_ptr() - .offset_from(self.slice.as_ptr()) as usize + ipv6.payload().payload.as_ptr().offset_from(slice.as_ptr()) as usize }; self.len_source = ipv6.payload().len_source; - self.slice = ipv6.payload().payload; self.result.net = Some(NetSlice::Ipv6(ipv6)); // only try to decode the transport layer if the payload @@ -263,38 +290,41 @@ impl<'a> SlicedPacketCursor<'a> { } else { //parse the data bellow match payload.ip_number { - ip_number::ICMP => self.slice_icmp4().map_err(Len), - ip_number::UDP => self.slice_udp().map_err(Len), - ip_number::TCP => self.slice_tcp().map_err(|err| { + ip_number::ICMP => self.slice_icmp4(payload.payload).map_err(Len), + ip_number::UDP => self.slice_udp(payload.payload).map_err(Len), + ip_number::TCP => self.slice_tcp(payload.payload).map_err(|err| { use err::tcp::HeaderSliceError as I; match err { I::Len(err) => Len(err), I::Content(err) => Tcp(err), } }), - ip_number::IPV6_ICMP => self.slice_icmp6().map_err(Len), + ip_number::IPV6_ICMP => self.slice_icmp6(payload.payload).map_err(Len), _ => Ok(self.result), } } } - pub fn slice_arp(mut self) -> Result, err::packet::SliceError> { - let result = ArpPacketSlice::from_slice(self.slice).map_err(|mut err| { + pub fn slice_arp( + mut self, + slice: &'a [u8], + ) -> Result, err::packet::SliceError> { + let result = ArpPacketSlice::from_slice(slice).map_err(|mut err| { err.layer_start_offset += self.offset; err::packet::SliceError::Len(err) })?; //set the new data - self.move_by(result.slice().len()); + self.offset += result.slice().len(); self.result.net = Some(NetSlice::Arp(result.clone())); Ok(self.result) } - pub fn slice_icmp4(mut self) -> Result, err::LenError> { + pub fn slice_icmp4(mut self, slice: &'a [u8]) -> Result, err::LenError> { use crate::TransportSlice::*; - let result = Icmpv4Slice::from_slice(self.slice).map_err(|mut err| { + let result = Icmpv4Slice::from_slice(slice).map_err(|mut err| { err.layer_start_offset += self.offset; if LenSource::Slice == err.len_source { err.len_source = self.len_source; @@ -303,16 +333,16 @@ impl<'a> SlicedPacketCursor<'a> { })?; //set the new data - self.move_by(result.slice().len()); + self.offset += result.slice().len(); self.result.transport = Some(Icmpv4(result.clone())); Ok(self.result) } - pub fn slice_icmp6(mut self) -> Result, err::LenError> { + pub fn slice_icmp6(mut self, slice: &'a [u8]) -> Result, err::LenError> { use crate::TransportSlice::*; - let result = Icmpv6Slice::from_slice(self.slice).map_err(|mut err| { + let result = Icmpv6Slice::from_slice(slice).map_err(|mut err| { err.layer_start_offset += self.offset; if LenSource::Slice == err.len_source { err.len_source = self.len_source; @@ -321,17 +351,17 @@ impl<'a> SlicedPacketCursor<'a> { })?; //set the new data - self.move_by(result.slice().len()); + self.offset += result.slice().len(); self.result.transport = Some(Icmpv6(result.clone())); //done Ok(self.result) } - pub fn slice_udp(mut self) -> Result, err::LenError> { + pub fn slice_udp(mut self, slice: &'a [u8]) -> Result, err::LenError> { use crate::TransportSlice::*; - let result = UdpSlice::from_slice(self.slice).map_err(|mut err| { + let result = UdpSlice::from_slice(slice).map_err(|mut err| { err.layer_start_offset += self.offset; if LenSource::Slice == err.len_source { err.len_source = self.len_source; @@ -340,17 +370,20 @@ impl<'a> SlicedPacketCursor<'a> { })?; //set the new data - self.move_by(result.slice().len()); + self.offset += result.slice().len(); self.result.transport = Some(Udp(result)); // done Ok(self.result) } - pub fn slice_tcp(mut self) -> Result, err::tcp::HeaderSliceError> { + pub fn slice_tcp( + mut self, + slice: &'a [u8], + ) -> Result, err::tcp::HeaderSliceError> { use crate::TransportSlice::*; - let result = TcpSlice::from_slice(self.slice).map_err(|mut err| { + let result = TcpSlice::from_slice(slice).map_err(|mut err| { use err::tcp::HeaderSliceError::Len; if let Len(err) = &mut err { err.layer_start_offset += self.offset; @@ -362,7 +395,7 @@ impl<'a> SlicedPacketCursor<'a> { })?; //set the new data - self.move_by(result.slice().len()); + self.offset += result.slice().len(); self.result.transport = Some(Tcp(result)); // done diff --git a/etherparse/src/test_gens/mod.rs b/etherparse/src/test_gens/mod.rs index 48ddeb60..82bbf5e3 100644 --- a/etherparse/src/test_gens/mod.rs +++ b/etherparse/src/test_gens/mod.rs @@ -19,6 +19,16 @@ prop_compose! { } } +prop_compose! { + pub fn ether_type_unknown() + (ether_type in ether_type_any().prop_filter("ether_type must be unknown", + |v| !ETHERNET_KNOWN_ETHER_TYPES.iter().any(|&x| v == &x))) + -> EtherType + { + ether_type + } +} + prop_compose! { pub fn vlan_id_any() (value in 0..=0b0000_1111_1111_1111u16) @@ -156,6 +166,7 @@ pub static ETHERNET_KNOWN_ETHER_TYPES: &'static [EtherType] = &[ ether_type::VLAN_TAGGED_FRAME, ether_type::PROVIDER_BRIDGING, ether_type::VLAN_DOUBLE_TAGGED_FRAME, + ether_type::MACSEC, ]; prop_compose! { @@ -226,6 +237,109 @@ prop_compose! { } } +prop_compose! { + pub fn macsec_an_any()( + an in 0u8..=0b11, + ) -> MacsecAn + { + MacsecAn::try_from(an).unwrap() + } +} + +prop_compose! { + pub fn macsec_short_len_any()( + sl in 0u8..=0b0011_1111, + ) -> MacsecShortLen + { + MacsecShortLen::try_from(sl).unwrap() + } +} + +prop_compose! { + pub fn macsec_any()( + endstation_id in any::(), + scb in any::(), + encrypted in any::(), + userdata_changed in any::(), + an in macsec_an_any(), + short_len in macsec_short_len_any(), + packet_nr in any::(), + sci_present in any::(), + sci in any::(), + next_ether_type in ether_type_any() + ) -> MacsecHeader + { + MacsecHeader { + ptype: if encrypted { + if userdata_changed { + MacsecPType::Encrypted + } else { + MacsecPType::EncryptedUnmodified + } + } else { + if userdata_changed { + MacsecPType::Modified + } else { + MacsecPType::Unmodified(next_ether_type) + } + }, + endstation_id, + scb, + an, + short_len, + packet_nr, + sci: if sci_present { + Some(sci) + } else { + None + }, + } + } +} + +prop_compose! { + pub fn macsec_unknown()( + endstation_id in any::(), + scb in any::(), + encrypted in any::(), + userdata_changed in any::(), + an in macsec_an_any(), + short_len in macsec_short_len_any(), + packet_nr in any::(), + sci_present in any::(), + sci in any::(), + next_ether_type in ether_type_any().prop_filter("ether_type must be unknown", + |v| !ETHERNET_KNOWN_ETHER_TYPES.iter().any(|&x| v == &x))) + -> MacsecHeader + { + MacsecHeader { + ptype: if encrypted { + if userdata_changed { + MacsecPType::Encrypted + } else { + MacsecPType::EncryptedUnmodified + } + } else { + if userdata_changed { + MacsecPType::Modified + } else { + MacsecPType::Unmodified(next_ether_type) + } + }, + endstation_id, + scb, + an, + short_len, + packet_nr, + sci: if sci_present { + Some(sci) + } else { + None + }, + } + } +} + prop_compose! { pub fn arp_packet_any() ( diff --git a/etherparse/src/test_packet.rs b/etherparse/src/test_packet.rs index 73b16fbe..f3e66f29 100644 --- a/etherparse/src/test_packet.rs +++ b/etherparse/src/test_packet.rs @@ -1,18 +1,28 @@ use crate::*; use alloc::vec::Vec; +use arrayvec::ArrayVec; #[derive(Clone)] pub(crate) struct TestPacket { pub link: Option, - pub vlan: Option, + pub link_exts: ArrayVec, pub net: Option, pub transport: Option, } impl TestPacket { + pub fn has_arp(&self) -> bool { + self.net.as_ref().map(|v| v.is_arp()).unwrap_or(false) + } + pub fn len(&self, payload: &[u8]) -> usize { self.link.as_ref().map_or(0, |x| x.header_len()) - + self.vlan.as_ref().map_or(0, |x| x.header_len()) + + self + .link_exts + .as_ref() + .iter() + .map(|x| x.header_len()) + .sum::() + self.net.as_ref().map_or(0, |x| x.header_len()) + self.transport.as_ref().map_or(0, |x| x.header_len()) + payload.len() @@ -23,8 +33,11 @@ impl TestPacket { if let Some(link) = &self.link { link.write(&mut result).unwrap(); } - if let Some(vlan) = &self.vlan { - vlan.write(&mut result).unwrap(); + for e in &self.link_exts { + match e { + LinkExtHeader::Vlan(s) => s.write(&mut result).unwrap(), + LinkExtHeader::Macsec(m) => m.write(&mut result).unwrap(), + } } if let Some(net) = &self.net { match net { @@ -49,27 +62,51 @@ impl TestPacket { } pub fn set_ether_type(&mut self, ether_type: EtherType) { - if let Some(vlan) = &mut self.vlan { - use VlanHeader::*; - match vlan { - Single(single) => { - single.ether_type = ether_type; + let mut next = ether_type; + for e in self.link_exts.iter_mut().rev() { + match e { + LinkExtHeader::Vlan(s) => { + s.ether_type = next; + if next == ether_type::VLAN_TAGGED_FRAME { + next = ether_type::VLAN_DOUBLE_TAGGED_FRAME; + } else { + next = ether_type::VLAN_TAGGED_FRAME; + } } - Double(double) => { - double.inner.ether_type = ether_type; + LinkExtHeader::Macsec(m) => { + m.ptype = MacsecPType::Unmodified(next); + next = ether_type::MACSEC; } } - } else if let Some(link) = &mut self.link { + } + if let Some(link) = &mut self.link { match link { - LinkHeader::Ethernet2(ethernet) => ethernet.ether_type = ether_type, - LinkHeader::LinuxSll(linux_sll) => { - linux_sll.protocol_type.change_value(ether_type.0) - } + LinkHeader::Ethernet2(ethernet) => ethernet.ether_type = next, + LinkHeader::LinuxSll(linux_sll) => linux_sll.protocol_type.change_value(next.0), } } } pub fn set_payload_len(&mut self, payload_len: usize) { + // link extensions + { + let mut last_len = self.net.as_ref().map(|v| v.header_len()).unwrap_or(0) + + self.transport.as_ref().map(|v| v.header_len()).unwrap_or(0) + + payload_len; + for ext in self.link_exts.iter_mut().rev() { + match ext { + LinkExtHeader::Vlan(h) => { + last_len += h.header_len(); + } + LinkExtHeader::Macsec(h) => { + h.set_payload_len(last_len); + last_len += h.header_len(); + } + } + } + } + + // net layer use NetHeaders::*; match &mut self.net { None => {} @@ -94,6 +131,7 @@ impl TestPacket { Some(Arp(_)) => {} } + // transport layer use TransportHeader::*; match &mut self.transport { None => {} @@ -107,7 +145,7 @@ impl TestPacket { } /// Set the length relative to the end of the ip headers. - pub fn set_payload_le_from_ip_on(&mut self, payload_len_from_ip_on: isize) { + pub fn set_payload_len_ip(&mut self, payload_len_from_ip_on: isize) { use NetHeaders::*; match self.net.as_mut().unwrap() { Ipv4(ref mut header, ref mut exts) => { @@ -126,6 +164,22 @@ impl TestPacket { } } + /// Set the length relative to the end of the link extensions. + pub fn set_payload_len_link_ext(&mut self, payload_len_from_ext_on: usize) { + let mut payload_len = payload_len_from_ext_on; + for ext in self.link_exts.iter_mut().rev() { + match ext { + LinkExtHeader::Vlan(v) => { + payload_len += v.header_len(); + } + LinkExtHeader::Macsec(h) => { + h.set_payload_len(payload_len); + payload_len += h.header_len(); + } + } + } + } + pub fn is_ip_payload_fragmented(&self) -> bool { self.net.as_ref().map_or(false, |net| match net { NetHeaders::Ipv4(h, _) => h.is_fragmenting_payload(),