diff --git a/README.md b/README.md index 045900ef..bd55a23c 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,16 @@ This is the faster option if your code is not interested in all fields of all th Depending from which point downward you want to slice a package check out the functions: -* [`SlicedPacket::from_ethernet`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards -* [`SlicedPacket::from_linux_sll`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SlicedPacket.html#method.from_linux_sll) for parsing from a Linux Cooked Capture v1 (SLL) downwards -* [`SlicedPacket::from_ether_type`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SlicedPacket.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header -* [`SlicedPacket::from_ip`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SlicedPacket.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards +* [`SlicedPacket::from_ethernet`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards +* [`SlicedPacket::from_linux_sll`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SlicedPacket.html#method.from_linux_sll) for parsing from a Linux Cooked Capture v1 (SLL) downwards +* [`SlicedPacket::from_ether_type`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SlicedPacket.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header +* [`SlicedPacket::from_ip`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SlicedPacket.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards In case you want to parse cut off packets (e.g. packets returned in in ICMP message) you can use the "lax" parsing methods: -* [`LaxSlicedPacket::from_ethernet`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxSlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards -* [`LaxSlicedPacket::from_ether_type`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxSlicedPacket.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header -* [`LaxSlicedPacket::from_ip`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxSlicedPacket.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards +* [`LaxSlicedPacket::from_ethernet`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxSlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards +* [`LaxSlicedPacket::from_ether_type`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxSlicedPacket.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header +* [`LaxSlicedPacket::from_ip`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxSlicedPacket.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards ### Deserializing all headers into structs This option deserializes all known headers and transfers their contents to header structs. @@ -88,33 +88,33 @@ This option is slower then slicing when only few fields are accessed. But it can Depending from which point downward you want to unpack a package check out the functions -* [`PacketHeaders::from_ethernet_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.PacketHeaders.html#method.from_ethernet_slice) for parsing from an Ethernet II header downwards -* [`PacketHeaders::from_ether_type`](https://docs.rs/etherparse/0.18.2/etherparse/struct.PacketHeaders.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header -* [`PacketHeaders::from_ip_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.PacketHeaders.html#method.from_ip_slice) for parsing from an IPv4 or IPv6 downwards +* [`PacketHeaders::from_ethernet_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.PacketHeaders.html#method.from_ethernet_slice) for parsing from an Ethernet II header downwards +* [`PacketHeaders::from_ether_type`](https://docs.rs/etherparse/0.19.0/etherparse/struct.PacketHeaders.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header +* [`PacketHeaders::from_ip_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.PacketHeaders.html#method.from_ip_slice) for parsing from an IPv4 or IPv6 downwards In case you want to parse cut off packets (e.g. packets returned in in ICMP message) you can use the "lax" parsing methods: -* [`LaxPacketHeaders::from_ethernet`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxPacketHeaders.html#method.from_ethernet) for parsing from an Ethernet II header downwards -* [`LaxPacketHeaders::from_linux_sll`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxPacketHeaders.html#method.from_linux_sll) for parsing from a Linux Cooked Capture v1 (SLL) downwards -* [`LaxPacketHeaders::from_ether_type`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxPacketHeaders.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header -* [`LaxPacketHeaders::from_ip`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxPacketHeaders.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards +* [`LaxPacketHeaders::from_ethernet`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxPacketHeaders.html#method.from_ethernet) for parsing from an Ethernet II header downwards +* [`LaxPacketHeaders::from_linux_sll`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxPacketHeaders.html#method.from_linux_sll) for parsing from a Linux Cooked Capture v1 (SLL) downwards +* [`LaxPacketHeaders::from_ether_type`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxPacketHeaders.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header +* [`LaxPacketHeaders::from_ip`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxPacketHeaders.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards ### Manually slicing only one packet layer It is also possible to only slice one packet layer: -* [`Ethernet2Slice::from_slice_without_fcs`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2Slice.html#method.from_slice_without_fcs) & [`Ethernet2Slice::from_slice_with_crc32_fcs`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2Slice.html#method.from_slice_with_crc32_fcs) -* [`LinuxSllSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LinuxSllSlice.html#method.from_slice) -* [`SingleVlanSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SingleVlanSlice.html#method.from_slice) -* [`MacsecSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.MacsecSlice.html#method.from_slice) -* [`ArpPacketSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.ArpPacketSlice.html#method.from_slice) -* [`IpSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/enum.IpSlice.html#method.from_slice) & [`LaxIpSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/enum.LaxIpSlice.html#method.from_slice) -* [`Ipv4Slice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Slice.html#method.from_slice) & [`LaxIpv4Slice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxIpv4Slice.html#method.from_slice) -* [`Ipv6Slice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Slice.html#method.from_slice) & [`LaxIpv6Slice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LaxIpv6Slice.html#method.from_slice) -* [`UdpSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpSlice.html#method.from_slice) & [`UdpSlice::from_slice_lax`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpSlice.html#method.from_slice_lax) -* [`TcpSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.TcpSlice.html#method.from_slice) -* [`Icmpv4Slice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv4Slice.html#method.from_slice) -* [`Icmpv6Slice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv6Slice.html#method.from_slice) +* [`Ethernet2Slice::from_slice_without_fcs`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2Slice.html#method.from_slice_without_fcs) & [`Ethernet2Slice::from_slice_with_crc32_fcs`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2Slice.html#method.from_slice_with_crc32_fcs) +* [`LinuxSllSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LinuxSllSlice.html#method.from_slice) +* [`SingleVlanSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SingleVlanSlice.html#method.from_slice) +* [`MacsecSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.MacsecSlice.html#method.from_slice) +* [`ArpPacketSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.ArpPacketSlice.html#method.from_slice) +* [`IpSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/enum.IpSlice.html#method.from_slice) & [`LaxIpSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/enum.LaxIpSlice.html#method.from_slice) +* [`Ipv4Slice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Slice.html#method.from_slice) & [`LaxIpv4Slice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxIpv4Slice.html#method.from_slice) +* [`Ipv6Slice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Slice.html#method.from_slice) & [`LaxIpv6Slice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LaxIpv6Slice.html#method.from_slice) +* [`UdpSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpSlice.html#method.from_slice) & [`UdpSlice::from_slice_lax`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpSlice.html#method.from_slice_lax) +* [`TcpSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.TcpSlice.html#method.from_slice) +* [`Icmpv4Slice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv4Slice.html#method.from_slice) +* [`Icmpv6Slice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv6Slice.html#method.from_slice) The resulting data types allow access to both the header(s) and the payload of the layer and will automatically limit the length of payload if the layer has a length field limiting the @@ -126,39 +126,39 @@ an IPv6 header). It is also possible just to parse headers. Have a look at the documentation for the following \[NAME\]HeaderSlice.from_slice methods, if you want to just slice the header: -* [`Ethernet2HeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2HeaderSlice.html#method.from_slice) -* [`LinuxSllHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LinuxSllHeaderSlice.html#method.from_slice) -* [`SingleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SingleVlanHeaderSlice.html#method.from_slice) -* [`MacsecHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.MacsecHeaderSlice.html#method.from_slice) -* [`Ipv4HeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4HeaderSlice.html#method.from_slice) -* [`Ipv4ExtensionsSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4ExtensionsSlice.html#method.from_slice) -* [`Ipv6HeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6HeaderSlice.html#method.from_slice) -* [`Ipv6ExtensionsSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6ExtensionsSlice.html#method.from_slice) -* [`Ipv6RawExtHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6RawExtHeaderSlice.html#method.from_slice) -* [`IpAuthHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.IpAuthHeaderSlice.html#method.from_slice) -* [`Ipv6FragmentHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6FragmentHeaderSlice.html#method.from_slice) -* [`UdpHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpHeaderSlice.html#method.from_slice) -* [`TcpHeaderSlice::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.TcpHeaderSlice.html#method.from_slice) +* [`Ethernet2HeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2HeaderSlice.html#method.from_slice) +* [`LinuxSllHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LinuxSllHeaderSlice.html#method.from_slice) +* [`SingleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SingleVlanHeaderSlice.html#method.from_slice) +* [`MacsecHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.MacsecHeaderSlice.html#method.from_slice) +* [`Ipv4HeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4HeaderSlice.html#method.from_slice) +* [`Ipv4ExtensionsSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4ExtensionsSlice.html#method.from_slice) +* [`Ipv6HeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6HeaderSlice.html#method.from_slice) +* [`Ipv6ExtensionsSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6ExtensionsSlice.html#method.from_slice) +* [`Ipv6RawExtHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6RawExtHeaderSlice.html#method.from_slice) +* [`IpAuthHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.IpAuthHeaderSlice.html#method.from_slice) +* [`Ipv6FragmentHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6FragmentHeaderSlice.html#method.from_slice) +* [`UdpHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpHeaderSlice.html#method.from_slice) +* [`TcpHeaderSlice::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.TcpHeaderSlice.html#method.from_slice) And for deserialization into the corresponding header structs have a look at: -* [`Ethernet2Header::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2Header.html#method.read) & [`Ethernet2Header::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2Header.html#method.from_slice) -* [`LinuxSllHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LinuxSllHeader.html#method.read) & [`LinuxSllHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LinuxSllHeader.html#method.from_slice) -* [`SingleVlanHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SingleVlanHeader.html#method.read) & [`SingleVlanHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SingleVlanHeader.html#method.from_slice) -* [`MacsecHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.MacsecHeader.html#method.read) & [`MacsecHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.MacsecHeader.html#method.from_slice) -* [`ArpPacket::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.ArpPacket.html#method.read) & [`ArpPacket::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.ArpPacket.html#method.from_slice) -* [`IpHeaders::read`](https://docs.rs/etherparse/0.18.2/etherparse/enum.IpHeaders.html#method.read) & [`IpHeaders::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/enum.IpHeaders.html#method.from_slice) -* [`Ipv4Header::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Header.html#method.read) & [`Ipv4Header::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Header.html#method.from_slice) -* [`Ipv4Extensions::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Extensions.html#method.read) & [`Ipv4Extensions::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Extensions.html#method.from_slice) -* [`Ipv6Header::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Header.html#method.read) & [`Ipv6Header::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Header.html#method.from_slice) -* [`Ipv6Extensions::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Extensions.html#method.read) & [`Ipv6Extensions::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Extensions.html#method.from_slice) -* [`Ipv6RawExtHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6RawExtHeader.html#method.read) & [`Ipv6RawExtHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6RawExtHeader.html#method.from_slice) -* [`IpAuthHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.IpAuthHeader.html#method.read) & [`IpAuthHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.IpAuthHeader.html#method.from_slice) -* [`Ipv6FragmentHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6FragmentHeader.html#method.read) & [`Ipv6FragmentHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6FragmentHeader.html#method.from_slice) -* [`UdpHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpHeader.html#method.read) & [`UdpHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpHeader.html#method.from_slice) -* [`TcpHeader::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.TcpHeader.html#method.read) & [`TcpHeader::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.TcpHeader.html#method.from_slice) -* [`Icmpv4Header::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv4Header.html#method.read) & [`Icmpv4Header::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv4Header.html#method.from_slice) -* [`Icmpv6Header::read`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv6Header.html#method.read) & [`Icmpv6Header::from_slice`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv6Header.html#method.from_slice) +* [`Ethernet2Header::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2Header.html#method.read) & [`Ethernet2Header::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2Header.html#method.from_slice) +* [`LinuxSllHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LinuxSllHeader.html#method.read) & [`LinuxSllHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LinuxSllHeader.html#method.from_slice) +* [`SingleVlanHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SingleVlanHeader.html#method.read) & [`SingleVlanHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SingleVlanHeader.html#method.from_slice) +* [`MacsecHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.MacsecHeader.html#method.read) & [`MacsecHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.MacsecHeader.html#method.from_slice) +* [`ArpPacket::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.ArpPacket.html#method.read) & [`ArpPacket::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.ArpPacket.html#method.from_slice) +* [`IpHeaders::read`](https://docs.rs/etherparse/0.19.0/etherparse/enum.IpHeaders.html#method.read) & [`IpHeaders::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/enum.IpHeaders.html#method.from_slice) +* [`Ipv4Header::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Header.html#method.read) & [`Ipv4Header::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Header.html#method.from_slice) +* [`Ipv4Extensions::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Extensions.html#method.read) & [`Ipv4Extensions::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Extensions.html#method.from_slice) +* [`Ipv6Header::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Header.html#method.read) & [`Ipv6Header::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Header.html#method.from_slice) +* [`Ipv6Extensions::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Extensions.html#method.read) & [`Ipv6Extensions::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Extensions.html#method.from_slice) +* [`Ipv6RawExtHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6RawExtHeader.html#method.read) & [`Ipv6RawExtHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6RawExtHeader.html#method.from_slice) +* [`IpAuthHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.IpAuthHeader.html#method.read) & [`IpAuthHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.IpAuthHeader.html#method.from_slice) +* [`Ipv6FragmentHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6FragmentHeader.html#method.read) & [`Ipv6FragmentHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6FragmentHeader.html#method.from_slice) +* [`UdpHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpHeader.html#method.read) & [`UdpHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpHeader.html#method.from_slice) +* [`TcpHeader::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.TcpHeader.html#method.read) & [`TcpHeader::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.TcpHeader.html#method.from_slice) +* [`Icmpv4Header::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv4Header.html#method.read) & [`Icmpv4Header::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv4Header.html#method.from_slice) +* [`Icmpv6Header::read`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv6Header.html#method.read) & [`Icmpv6Header::from_slice`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv6Header.html#method.from_slice) ## How to generate fake packet data? @@ -193,30 +193,30 @@ builder.write(&mut result, &payload).unwrap(); There is also an [example for TCP packets](etherparse/examples/write_tcp.rs) available. -Check out the [PacketBuilder documentation](https://docs.rs/etherparse/0.18.2/etherparse/struct.PacketBuilder.html) for more information. +Check out the [PacketBuilder documentation](https://docs.rs/etherparse/0.19.0/etherparse/struct.PacketBuilder.html) for more information. ### Manually serializing each header Alternatively it is possible to manually build a packet ([example](etherparse/examples/write_ipv4_udp.rs)). Generally each struct representing a header has a "write" method that allows it to be serialized. These write methods sometimes automatically calculate checksums and fill them in. In case this is unwanted behavior (e.g. if you want to generate a packet with an invalid checksum), it is also possible to call a "write_raw" method that will simply serialize the data without doing checksum calculations. Read the documentations of the different methods for a more details: -* [`Ethernet2Header::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2Header.html#method.to_bytes) & [`Ethernet2Header::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ethernet2Header.html#method.write) -* [`LinuxSllHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LinuxSllHeader.html#method.to_bytes) & [`LinuxSllHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.LinuxSllHeader.html#method.write) -* [`SingleVlanHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SingleVlanHeader.html#method.to_bytes) & [`SingleVlanHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.SingleVlanHeader.html#method.write) -* [`MacsecHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.MacsecHeader.html#method.to_bytes) & [`MacsecHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.MacsecHeader.html#method.write) -* [`ArpPacket::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.ArpPacket.html#method.to_bytes) & [`ArpPacket::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.ArpPacket.html#method.write) -* [`ArpEthIpv4Packet::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.ArpEthIpv4Packet.html#method.to_bytes) -* [`Ipv4Header::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Header.html#method.to_bytes) & [`Ipv4Header::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Header.html#method.write) & [`Ipv4Header::write_raw`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Header.html#method.write_raw) -* [`Ipv4Extensions::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv4Extensions.html#method.write) -* [`Ipv6Header::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Header.html#method.to_bytes) & [`Ipv6Header::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Header.html#method.write) -* [`Ipv6Extensions::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6Extensions.html#method.write) -* [`Ipv6RawExtHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6RawExtHeader.html#method.to_bytes) & [`Ipv6RawExtHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6RawExtHeader.html#method.write) -* [`IpAuthHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.IpAuthHeader.html#method.to_bytes) & [`IpAuthHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.IpAuthHeader.html#method.write) -* [`Ipv6FragmentHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6FragmentHeader.html#method.to_bytes) & [`Ipv6FragmentHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Ipv6FragmentHeader.html#method.write) -* [`UdpHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpHeader.html#method.to_bytes) & [`UdpHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.UdpHeader.html#method.write) -* [`TcpHeader::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.TcpHeader.html#method.to_bytes) & [`TcpHeader::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.TcpHeader.html#method.write) -* [`Icmpv4Header::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv4Header.html#method.to_bytes) & [`Icmpv4Header::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv4Header.html#method.write) -* [`Icmpv6Header::to_bytes`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv6Header.html#method.to_bytes) & [`Icmpv6Header::write`](https://docs.rs/etherparse/0.18.2/etherparse/struct.Icmpv6Header.html#method.write) +* [`Ethernet2Header::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2Header.html#method.to_bytes) & [`Ethernet2Header::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ethernet2Header.html#method.write) +* [`LinuxSllHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LinuxSllHeader.html#method.to_bytes) & [`LinuxSllHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.LinuxSllHeader.html#method.write) +* [`SingleVlanHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SingleVlanHeader.html#method.to_bytes) & [`SingleVlanHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.SingleVlanHeader.html#method.write) +* [`MacsecHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.MacsecHeader.html#method.to_bytes) & [`MacsecHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.MacsecHeader.html#method.write) +* [`ArpPacket::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.ArpPacket.html#method.to_bytes) & [`ArpPacket::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.ArpPacket.html#method.write) +* [`ArpEthIpv4Packet::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.ArpEthIpv4Packet.html#method.to_bytes) +* [`Ipv4Header::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Header.html#method.to_bytes) & [`Ipv4Header::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Header.html#method.write) & [`Ipv4Header::write_raw`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Header.html#method.write_raw) +* [`Ipv4Extensions::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv4Extensions.html#method.write) +* [`Ipv6Header::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Header.html#method.to_bytes) & [`Ipv6Header::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Header.html#method.write) +* [`Ipv6Extensions::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6Extensions.html#method.write) +* [`Ipv6RawExtHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6RawExtHeader.html#method.to_bytes) & [`Ipv6RawExtHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6RawExtHeader.html#method.write) +* [`IpAuthHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.IpAuthHeader.html#method.to_bytes) & [`IpAuthHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.IpAuthHeader.html#method.write) +* [`Ipv6FragmentHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6FragmentHeader.html#method.to_bytes) & [`Ipv6FragmentHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Ipv6FragmentHeader.html#method.write) +* [`UdpHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpHeader.html#method.to_bytes) & [`UdpHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.UdpHeader.html#method.write) +* [`TcpHeader::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.TcpHeader.html#method.to_bytes) & [`TcpHeader::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.TcpHeader.html#method.write) +* [`Icmpv4Header::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv4Header.html#method.to_bytes) & [`Icmpv4Header::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv4Header.html#method.write) +* [`Icmpv6Header::to_bytes`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv6Header.html#method.to_bytes) & [`Icmpv6Header::write`](https://docs.rs/etherparse/0.19.0/etherparse/struct.Icmpv6Header.html#method.write) ## References * Darpa Internet Program Protocol Specification [RFC 791](https://tools.ietf.org/html/rfc791) diff --git a/etherparse/src/transport/icmp_echo_header.rs b/etherparse/src/transport/icmp_echo_header.rs index ba139a9b..b01ff5de 100644 --- a/etherparse/src/transport/icmp_echo_header.rs +++ b/etherparse/src/transport/icmp_echo_header.rs @@ -7,7 +7,7 @@ /// node SHOULD also implement an application-layer interface for /// originating Echo Requests and receiving Echo Replies, for diagnostic /// purposes. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct IcmpEchoHeader { /// An identifier to aid in matching Echo Replies to Echo Requests. May be zero. pub id: u16, diff --git a/etherparse/src/transport/icmpv6/dest_unreachable_code.rs b/etherparse/src/transport/icmpv6/dest_unreachable_code.rs index 4f72d1ba..0be3a6ce 100644 --- a/etherparse/src/transport/icmpv6/dest_unreachable_code.rs +++ b/etherparse/src/transport/icmpv6/dest_unreachable_code.rs @@ -10,7 +10,7 @@ use super::*; /// that cannot be delivered to its destination address for reasons other /// than congestion. (An ICMPv6 message MUST NOT be generated if a /// packet is dropped due to congestion.) -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum DestUnreachableCode { /// No route to destination NoRoute = 0, diff --git a/etherparse/src/transport/icmpv6/mod.rs b/etherparse/src/transport/icmpv6/mod.rs index 195abd14..f8143dc3 100644 --- a/etherparse/src/transport/icmpv6/mod.rs +++ b/etherparse/src/transport/icmpv6/mod.rs @@ -10,8 +10,11 @@ pub use parameter_problem_header::*; mod time_exceeded_code; pub use time_exceeded_code::*; -mod neighbour_discovery; -pub use neighbour_discovery::*; +mod neighbor_advertisement_header; +pub use neighbor_advertisement_header::*; + +mod router_advertisement_header; +pub use router_advertisement_header::*; /// The maximum number of bytes/octets the ICMPv6 part of a packet can contain. /// diff --git a/etherparse/src/transport/icmpv6/neighbor_advertisement_header.rs b/etherparse/src/transport/icmpv6/neighbor_advertisement_header.rs new file mode 100644 index 00000000..b1373b86 --- /dev/null +++ b/etherparse/src/transport/icmpv6/neighbor_advertisement_header.rs @@ -0,0 +1,103 @@ +/// ICMPv6 neighbor advertisement header (part of "Neighbor Discovery Protocol" +/// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct NeighborAdvertisementHeader { + pub router: bool, + pub solicited: bool, + pub r#override: bool, +} + +impl NeighborAdvertisementHeader { + /// Mask to read out the "router" flag out of the 5th byte of the ICMPv6 header. + pub const ROUTER_MASK: u8 = 0b10000000; + + /// Mask to read out the "solicited" flag out of the 5th byte of the ICMPv6 header. + pub const SOLICITED_MASK: u8 = 0b01000000; + + /// Mask to read out the "override" flag out of the 5th byte of the ICMPv6 header. + pub const OVERRIDE_MASK: u8 = 0b00100000; + + /// Decodes the header from the on the wire bytes. + pub fn from_bytes(bytes: [u8; 4]) -> Self { + let first_byte = bytes[0]; + + Self { + router: (first_byte & Self::ROUTER_MASK) == Self::ROUTER_MASK, + solicited: (first_byte & Self::SOLICITED_MASK) == Self::SOLICITED_MASK, + r#override: (first_byte & Self::OVERRIDE_MASK) == Self::OVERRIDE_MASK, + } + } + + /// Converts the header to the on the wire bytes. + pub fn to_bytes(&self) -> [u8; 4] { + let mut first_byte = 0u8; + + if self.router { + first_byte |= Self::ROUTER_MASK; + } + if self.solicited { + first_byte |= Self::SOLICITED_MASK; + } + if self.r#override { + first_byte |= Self::OVERRIDE_MASK; + } + + [first_byte, 0, 0, 0] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn to_and_from_bytes( + router in any::(), + solicited in any::(), + r#override in any::() + ) { + let bytes = NeighborAdvertisementHeader{ + router, + solicited, + r#override + }.to_bytes(); + assert_eq!( + NeighborAdvertisementHeader::from_bytes(bytes), + NeighborAdvertisementHeader { + router, + solicited, + r#override, + } + ); + } + } + + #[test] + fn reads_router_bit_correctly() { + assert!(NeighborAdvertisementHeader::from_bytes([0b10000000, 0, 0, 0]).router); + assert!(!NeighborAdvertisementHeader::from_bytes([0, 0, 0, 0]).router); + } + + #[test] + fn reads_solicited_bit_correctly() { + assert!(NeighborAdvertisementHeader::from_bytes([0b01000000, 0, 0, 0]).solicited); + assert!(!NeighborAdvertisementHeader::from_bytes([0, 0, 0, 0]).solicited); + } + + #[test] + fn reads_override_bit_correctly() { + assert!(NeighborAdvertisementHeader::from_bytes([0b00100000, 0, 0, 0]).r#override); + assert!(!NeighborAdvertisementHeader::from_bytes([0, 0, 0, 0]).r#override); + } + + #[test] + fn reads_combined_bit_correctly() { + let header = NeighborAdvertisementHeader::from_bytes([0b11100000, 0, 0, 0]); + + assert!(header.router); + assert!(header.solicited); + assert!(header.r#override); + } +} diff --git a/etherparse/src/transport/icmpv6/neighbour_discovery.rs b/etherparse/src/transport/icmpv6/neighbour_discovery.rs deleted file mode 100644 index 3790e34e..00000000 --- a/etherparse/src/transport/icmpv6/neighbour_discovery.rs +++ /dev/null @@ -1,70 +0,0 @@ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct NeighbourAdverisementHeader { - pub router: bool, - pub solicited: bool, - pub r#override: bool, -} - -const ROUTER_MASK: u8 = 0b10000000; -const SOLICITED_MASK: u8 = 0b01000000; -const OVERRIDE_MASK: u8 = 0b00100000; - -impl NeighbourAdverisementHeader { - pub fn from_bytes(bytes: [u8; 4]) -> Self { - let first_byte = bytes[0]; - - Self { - router: (first_byte & ROUTER_MASK) == ROUTER_MASK, - solicited: (first_byte & SOLICITED_MASK) == SOLICITED_MASK, - r#override: (first_byte & OVERRIDE_MASK) == OVERRIDE_MASK, - } - } - - pub fn to_bytes(&self) -> [u8; 4] { - let mut first_byte = 0u8; - - if self.router { - first_byte |= ROUTER_MASK; - } - if self.solicited { - first_byte |= SOLICITED_MASK; - } - if self.r#override { - first_byte |= OVERRIDE_MASK; - } - - [first_byte, 0, 0, 0] - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn reads_router_bit_correctly() { - assert!(NeighbourAdverisementHeader::from_bytes([0b10000000, 0, 0, 0]).router); - assert!(!NeighbourAdverisementHeader::from_bytes([0, 0, 0, 0]).router); - } - - #[test] - fn reads_solicited_bit_correctly() { - assert!(NeighbourAdverisementHeader::from_bytes([0b01000000, 0, 0, 0]).solicited); - assert!(!NeighbourAdverisementHeader::from_bytes([0, 0, 0, 0]).solicited); - } - - #[test] - fn reads_override_bit_correctly() { - assert!(NeighbourAdverisementHeader::from_bytes([0b00100000, 0, 0, 0]).r#override); - assert!(!NeighbourAdverisementHeader::from_bytes([0, 0, 0, 0]).r#override); - } - - #[test] - fn reads_combined_bit_correctly() { - let header = NeighbourAdverisementHeader::from_bytes([0b11100000, 0, 0, 0]); - - assert!(header.router); - assert!(header.solicited); - assert!(header.r#override); - } -} diff --git a/etherparse/src/transport/icmpv6/parameter_problem_code.rs b/etherparse/src/transport/icmpv6/parameter_problem_code.rs index 76164018..5f6195a6 100644 --- a/etherparse/src/transport/icmpv6/parameter_problem_code.rs +++ b/etherparse/src/transport/icmpv6/parameter_problem_code.rs @@ -3,7 +3,7 @@ use super::*; /// Code values for ICMPv6 parameter problem messages. /// /// Source: -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum ParameterProblemCode { /// Erroneous header field encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443)) ErroneousHeaderField = 0, diff --git a/etherparse/src/transport/icmpv6/parameter_problem_header.rs b/etherparse/src/transport/icmpv6/parameter_problem_header.rs index c1896810..1b3b1afe 100644 --- a/etherparse/src/transport/icmpv6/parameter_problem_header.rs +++ b/etherparse/src/transport/icmpv6/parameter_problem_header.rs @@ -1,7 +1,7 @@ use super::*; /// ICMPv6 parameter problem header. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct ParameterProblemHeader { /// The code can offer additional informations about what kind of parameter /// problem caused the error. diff --git a/etherparse/src/transport/icmpv6/router_advertisement_header.rs b/etherparse/src/transport/icmpv6/router_advertisement_header.rs new file mode 100644 index 00000000..ffd55539 --- /dev/null +++ b/etherparse/src/transport/icmpv6/router_advertisement_header.rs @@ -0,0 +1,109 @@ +/// ICMPv6 router advertisement header (part of "Neighbor Discovery Protocol" +/// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct RouterAdvertisementHeader { + /// The default value that should be placed in the Hop Count + /// field of the IP header for outgoing IP packets. + /// + /// A value of zero means unspecified (by this router). + pub cur_hop_limit: u8, + + /// "Managed address configuration" flag. + /// + /// When set, it indicates that addresses are available via + /// Dynamic Host Configuration Protocol [DHCPv6]. + /// + /// If the M flag is set, the O flag is redundant and + /// can be ignored because DHCPv6 will return all + /// available configuration information. + pub managed_address_config: bool, + + /// "Other configuration" flag. + /// + /// When set, it indicates that other configuration information + /// is available via DHCPv6. Examples of such information are + /// DNS-related information or information on other servers + /// within the network. + pub other_config: bool, + + /// The lifetime associated with the default router in units of + /// seconds. + /// + /// The field can contain values up to 65535 and receivers should + /// handle any value, while the sending rules in Section 6 of + /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861) limit + /// the lifetime to 9000 seconds. A Lifetime of 0 indicates that + /// the router is not a default router and SHOULD NOT appear on + /// the default router list. The Router Lifetime applies only to + /// the router's usefulness as a default router; it does not apply + /// to information contained in other message fields or options. + /// Options that need time limits for their information include + /// their own lifetime fields. + pub router_lifetime: u16, +} + +impl RouterAdvertisementHeader { + /// Mask to read out the "Managed Address Configuration" flag out of + /// the 5th byte of the ICMPv6 header. + pub const MANAGED_ADDRESS_CONFIG_MASK: u8 = 0b1000_0000; + + /// Mask to read out the "Other Configuration" flag out of the 5th + /// byte of the ICMPv6 header. + pub const OTHER_CONFIG_MASK: u8 = 0b0100_0000; + + /// Decodes the header from the on the wire bytes. + pub fn from_bytes(bytes: [u8; 4]) -> Self { + RouterAdvertisementHeader { + cur_hop_limit: bytes[0], + managed_address_config: 0 != bytes[1] & Self::MANAGED_ADDRESS_CONFIG_MASK, + other_config: 0 != bytes[1] & Self::OTHER_CONFIG_MASK, + router_lifetime: u16::from_be_bytes([bytes[2], bytes[3]]), + } + } + + /// Converts the header to the on the wire bytes. + pub fn to_bytes(&self) -> [u8; 4] { + let rl_be = self.router_lifetime.to_be_bytes(); + [ + self.cur_hop_limit, + (if self.managed_address_config { + Self::MANAGED_ADDRESS_CONFIG_MASK + } else { + 0 + } | if self.other_config { + Self::OTHER_CONFIG_MASK + } else { + 0 + }), + rl_be[0], + rl_be[1], + ] + } +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn to_and_from_bytes( + cur_hop_limit in any::(), + managed_address_config in any::(), + other_config in any::(), + router_lifetime in any::() + ) { + let expected = RouterAdvertisementHeader{ + cur_hop_limit, + managed_address_config, + other_config, + router_lifetime + }; + let actual = RouterAdvertisementHeader::from_bytes( + expected.to_bytes() + ); + assert_eq!(actual, expected); + } + } +} diff --git a/etherparse/src/transport/icmpv6/time_exceeded_code.rs b/etherparse/src/transport/icmpv6/time_exceeded_code.rs index 9f060304..a0e4b8f1 100644 --- a/etherparse/src/transport/icmpv6/time_exceeded_code.rs +++ b/etherparse/src/transport/icmpv6/time_exceeded_code.rs @@ -1,7 +1,7 @@ use super::*; /// Code values for ICMPv6 time exceeded message. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum TimeExceededCode { /// "hop limit exceeded in transit" HopLimitExceeded = 0, diff --git a/etherparse/src/transport/icmpv6_header.rs b/etherparse/src/transport/icmpv6_header.rs index e52e28d9..5d100cfb 100644 --- a/etherparse/src/transport/icmpv6_header.rs +++ b/etherparse/src/transport/icmpv6_header.rs @@ -184,10 +184,15 @@ impl Icmpv6Header { ), EchoRequest(echo) => return_4u8(TYPE_ECHO_REQUEST, 0, echo.to_bytes()), EchoReply(echo) => return_4u8(TYPE_ECHO_REPLY, 0, echo.to_bytes()), - NeighbourSoliciation => return_4u8(TYPE_NEIGHBOR_SOLICITATION, 0, [0; 4]), - NeighbourAdvertisement(header) => { + RouterSolicitation => return_trivial(TYPE_ROUTER_SOLICITATION, 0), + RouterAdvertisement(header) => { + return_4u8(TYPE_ROUTER_ADVERTISEMENT, 0, header.to_bytes()) + } + NeighborSolicitation => return_trivial(TYPE_NEIGHBOR_SOLICITATION, 0), + NeighborAdvertisement(header) => { return_4u8(TYPE_NEIGHBOR_ADVERTISEMENT, 0, header.to_bytes()) } + Redirect => return_trivial(TYPE_REDIRECT_MESSAGE, 0), } } } @@ -456,6 +461,9 @@ mod test { checksum in any::(), rand_u32 in any::(), rand_4bytes in any::<[u8;4]>(), + rand_bool0 in any::(), + rand_bool1 in any::(), + rand_bool2 in any::(), ) { use Icmpv6Type::*; @@ -543,6 +551,44 @@ mod test { with_5to8_bytes(TYPE_ECHO_REPLY, 0, rand_4bytes) ); + // neighbor solicitation + assert_eq!( + Icmpv6Header{ + icmp_type: NeighborSolicitation, + checksum + }.to_bytes(), + with_5to8_bytes(TYPE_NEIGHBOR_SOLICITATION, 0, [0;4]) + ); + + // neighbor advertisement + assert_eq!( + Icmpv6Header{ + icmp_type: NeighborAdvertisement( + NeighborAdvertisementHeader { + router: rand_bool0, + solicited: rand_bool1, + r#override: rand_bool2, + } + ), + checksum + }.to_bytes(), + with_5to8_bytes(TYPE_NEIGHBOR_ADVERTISEMENT, 0, [ + if rand_bool0 { + NeighborAdvertisementHeader::ROUTER_MASK + } else { + 0 + } | if rand_bool1 { + NeighborAdvertisementHeader::SOLICITED_MASK + } else { + 0 + } | if rand_bool2 { + NeighborAdvertisementHeader::OVERRIDE_MASK + } else { + 0 + }, 0, 0, 0 + ]) + ); + // unknown for type_u8 in 0..=u8::MAX { for code_u8 in 0..=u8::MAX { diff --git a/etherparse/src/transport/icmpv6_slice.rs b/etherparse/src/transport/icmpv6_slice.rs index b072de12..7be4a164 100644 --- a/etherparse/src/transport/icmpv6_slice.rs +++ b/etherparse/src/transport/icmpv6_slice.rs @@ -101,12 +101,12 @@ impl<'a> Icmpv6Slice<'a> { } TYPE_NEIGHBOR_SOLICITATION => { if 0 == self.code_u8() { - return NeighbourSoliciation; + return NeighborSolicitation; } } TYPE_NEIGHBOR_ADVERTISEMENT => { if 0 == self.code_u8() { - return NeighbourAdvertisement(NeighbourAdverisementHeader::from_bytes( + return NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes( self.bytes5to8(), )); } @@ -403,6 +403,34 @@ mod test { assert_unknown(TYPE_ECHO_REPLY, code_u8); } } + + // neighbor solicitation + { + // known code + assert_eq!( + Icmpv6Slice::from_slice(&gen_bytes(TYPE_NEIGHBOR_SOLICITATION, 0)).unwrap().icmp_type(), + NeighborSolicitation + ); + + // unknown codes + for code_u8 in 1..=u8::MAX { + assert_unknown(TYPE_NEIGHBOR_SOLICITATION, code_u8); + } + } + + // neighbor advertisement + { + // known code + assert_eq!( + Icmpv6Slice::from_slice(&gen_bytes(TYPE_NEIGHBOR_ADVERTISEMENT, 0)).unwrap().icmp_type(), + NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)) + ); + + // unknown codes + for code_u8 in 1..=u8::MAX { + assert_unknown(TYPE_NEIGHBOR_ADVERTISEMENT, code_u8); + } + } } } diff --git a/etherparse/src/transport/icmpv6_type.rs b/etherparse/src/transport/icmpv6_type.rs index e77183ef..e4490437 100644 --- a/etherparse/src/transport/icmpv6_type.rs +++ b/etherparse/src/transport/icmpv6_type.rs @@ -1,5 +1,6 @@ use crate::{ err::{ValueTooBigError, ValueType}, + icmpv6::RouterAdvertisementHeader, *, }; @@ -40,8 +41,11 @@ use crate::{ /// ParameterProblem(header) => println!("{:?}", header), /// EchoRequest(header) => println!("{:?}", header), /// EchoReply(header) => println!("{:?}", header), -/// NeighbourSoliciation => println!("NeighbourSoliciation"), -/// NeighbourAdvertisement(header) => println!("{:?}", header), +/// RouterSolicitation => println!("RouterSolicitation"), +/// RouterAdvertisement(header) => println!("{:?}", header), +/// NeighborSolicitation => println!("NeighborSolicitation"), +/// NeighborAdvertisement(header) => println!("{:?}", header), +/// Redirect => println!("Redirect"), /// } /// }, /// _ => {}, @@ -86,7 +90,7 @@ use crate::{ /// # ); /// # } /// ``` -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum Icmpv6Type { /// In case of an unknown icmp type is received the header elements of /// the first 8 bytes/octets are stored raw in this enum value. @@ -332,29 +336,91 @@ pub enum Icmpv6Type { /// entirely and unmodified in the ICMPv6 Echo Reply message. EchoReply(IcmpEchoHeader), - /// Requesting the link-layer address of a target node. + /// Router Solicitation message header (part of "Neighbor Discovery Protocol" + /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). /// - /// # What is part of the header for `Icmpv6Type::NeighbourSoliciation`? + /// # What is part of the header for `Icmpv6Type::RouterSolicitation`? /// - /// For the [`Icmpv6Type::NeighbourSoliciation`] type the first 8 bytes/octets + /// For the [`Icmpv6Type::RouterSolicitation`] type the first 8 bytes/octets /// of the ICMPv6 packet are part of the header. /// - /// The data part of the ICMP Neighbor Advertisement packet is part of the payload + /// The options part of the ICMP Router Solicitation packet is part of the payload /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the /// [`Icmpv6Header`]. /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - - /// | 135 | 0 | checksum (in Icmpv6Header) | | + /// | 133 | 0 | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | | ↓ /// +---------------------------------------------------------------+ - - /// | | | - /// | Target Address | part of payload + /// | Options ... | | part of payload /// | | ↓ + /// +----------------- - + /// ``` + /// + /// # RFC 4861 Description + /// + /// Hosts send Router Solicitations in order to prompt routers to + /// generate Router Advertisements quickly. + RouterSolicitation, + + /// Router Advertisement message header (part of "Neighbor Discovery Protocol" + /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). + /// + /// # What is part of the header for `Icmpv6Type::RouterAdvertisement`? + /// + /// For the [`Icmpv6Type::RouterAdvertisement`] type the first 8 bytes/octets + /// of the ICMPv6 packet are part of the header. + /// + /// The options part of the ICMP Router Advertisement packet is part of the payload + /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the + /// [`Icmpv6Header`]. + /// ```text + /// 0 1 2 3 4 + /// +---------------------------------------------------------------+ - + /// | 134 | 0 | checksum (in Icmpv6Header) | | + /// +---------------------------------------------------------------+ | part of header & type + /// | Cur Hop Limit |M|O| Reserved | Router Lifetime | ↓ + /// +---------------------------------------------------------------+ - + /// | Reachable Time | | + /// +---------------------------------------------------------------+ | + /// | Retrans Timer | | part of payload + /// +---------------------------------------------------------------+ | + /// | Options ... | ↓ + /// +----------------- - + /// ``` + /// + /// # RFC 4861 Description + /// + /// Routers send out Router Advertisement messages periodically, or in + /// response to Router Solicitations. + RouterAdvertisement(RouterAdvertisementHeader), + + /// Requesting the link-layer address of a target node (part of "Neighbor Discovery Protocol" + /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). + /// + /// # What is part of the header for `Icmpv6Type::NeighborSolicitation`? + /// + /// For the [`Icmpv6Type::NeighborSolicitation`] type the first 8 bytes/octets + /// of the ICMPv6 packet are part of the header. + /// + /// The target address & options of the ICMP Neighbor Solicitation packet is part + /// of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not + /// part of the [`Icmpv6Header`]. + /// ```text + /// 0 1 2 3 4 + /// +---------------------------------------------------------------+ - + /// | 135 | 0 | checksum (in Icmpv6Header) | | + /// +---------------------------------------------------------------+ | part of header & type + /// | | ↓ /// +---------------------------------------------------------------+ - - /// | Options ... - /// +----------------- + /// | | | + /// | Target Address | | + /// | | | part of payload + /// +---------------------------------------------------------------+ | + /// | Options ... ↓ + /// +----------------- - /// ``` /// /// # RFC 4861 Description @@ -364,21 +430,22 @@ pub enum Icmpv6Type { /// the target. Neighbor Solicitations are multicast when the node needs /// to resolve an address and unicast when the node seeks to verify the /// reachability of a neighbor. - NeighbourSoliciation, + NeighborSolicitation, - /// Response to a `NeighbourSoliciation` message (or sent proactively). + /// Header of "Neighbor Advertisement" message (part of "Neighbor Discovery Protocol" + /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). /// - /// # What is part of the header for `Icmpv6Type::NeighbourAdvertisement`? + /// # What is part of the header for `Icmpv6Type::NeighborAdvertisement`? /// - /// For the [`Icmpv6Type::NeighbourAdvertisement`] type the first 8 bytes/octets + /// For the [`Icmpv6Type::NeighborAdvertisement`] type the first 8 bytes/octets /// of the ICMPv6 packet are part of the header. This includes 3 bits for /// - router /// - solicited /// - override /// - /// The data part of the ICMP Neighbor Advertisement packet is part of the payload - /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the - /// [`Icmpv6Header`]. + /// The target address & options of the ICMP Neighbor Advertisement packet is part + /// of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not + /// part of the [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 @@ -388,11 +455,11 @@ pub enum Icmpv6Type { /// |R|S|O| | ↓ /// +---------------------------------------------------------------+ - /// | | | - /// | Target Address | part of payload - /// | | ↓ - /// +---------------------------------------------------------------+ - - /// | Options ... - /// +----------------- + /// | Target Address | | + /// | | | part of payload + /// +---------------------------------------------------------------+ | + /// | Options ... ↓ + /// +----------------- - /// ``` /// /// # RFC 4861 Description @@ -425,7 +492,47 @@ pub enum Icmpv6Type { /// addresses and in solicited proxy advertisements. /// It SHOULD be set in other solicited advertisements /// and in unsolicited advertisements. - NeighbourAdvertisement(icmpv6::NeighbourAdverisementHeader), + NeighborAdvertisement(icmpv6::NeighborAdvertisementHeader), + + /// Header of "Redirect" message (part of "Neighbor Discovery Protocol" + /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)). + /// + /// # What is part of the header for `Icmpv6Type::Redirect`? + /// + /// For the [`Icmpv6Type::Redirect`] type the first 8 bytes/octets + /// of the ICMPv6 packet are part of the header. + /// + /// The "target address", "destination address" & options part of the ICMP Redirect + /// packet is part of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) + /// and not part of the [`Icmpv6Header`]. + /// ```text + /// 0 1 2 3 4 + /// +---------------------------------------------------------------+ - + /// | 137 | 0 | checksum (in Icmpv6Header) | | + /// +---------------------------------------------------------------+ | part of header & type + /// | | ↓ + /// +---------------------------------------------------------------+ - + /// | | | + /// | Target Address | | + /// | | | + /// +---------------------------------------------------------------+ | + /// | | | part of payload + /// | Destination Address | | + /// | | | + /// +---------------------------------------------------------------+ | + /// | Options ... ↓ + /// +----------------- - - + /// ``` + /// + /// # RFC 4861 Description + /// + /// Routers send Redirect packets to inform a host of a better first-hop + /// node on the path to a destination. Hosts can be redirected to a + /// better first-hop router but can also be informed by a redirect that + /// the destination is in fact a neighbor. The latter is accomplished by + /// setting the ICMP Target Address equal to the ICMP Destination + /// Address. + Redirect, } impl Icmpv6Type { @@ -445,8 +552,11 @@ impl Icmpv6Type { ParameterProblem(_) => TYPE_PARAMETER_PROBLEM, EchoRequest(_) => TYPE_ECHO_REQUEST, EchoReply(_) => TYPE_ECHO_REPLY, - NeighbourSoliciation => TYPE_NEIGHBOR_SOLICITATION, - NeighbourAdvertisement { .. } => TYPE_NEIGHBOR_ADVERTISEMENT, + RouterSolicitation => TYPE_ROUTER_SOLICITATION, + RouterAdvertisement(_) => TYPE_ROUTER_ADVERTISEMENT, + NeighborSolicitation => TYPE_NEIGHBOR_SOLICITATION, + NeighborAdvertisement(_) => TYPE_NEIGHBOR_ADVERTISEMENT, + Redirect => TYPE_REDIRECT_MESSAGE, } } @@ -466,8 +576,11 @@ impl Icmpv6Type { ParameterProblem(header) => header.code.code_u8(), EchoRequest(_) => 0, EchoReply(_) => 0, - NeighbourSoliciation => 0, - NeighbourAdvertisement { .. } => 0, + RouterSolicitation => 0, + RouterAdvertisement(_) => 0, + NeighborSolicitation => 0, + NeighborAdvertisement(_) => 0, + Redirect => 0, } } @@ -540,12 +653,21 @@ impl Icmpv6Type { EchoReply(echo) => pseudo_sum .add_2bytes([TYPE_ECHO_REPLY, 0]) .add_4bytes(echo.to_bytes()), - NeighbourSoliciation => pseudo_sum + RouterSolicitation => pseudo_sum + .add_2bytes([TYPE_ROUTER_SOLICITATION, 0]) + .add_4bytes([0; 4]), + RouterAdvertisement(header) => pseudo_sum + .add_2bytes([TYPE_ROUTER_ADVERTISEMENT, 0]) + .add_4bytes(header.to_bytes()), + NeighborSolicitation => pseudo_sum .add_2bytes([TYPE_NEIGHBOR_SOLICITATION, 0]) .add_4bytes([0; 4]), - NeighbourAdvertisement(header) => pseudo_sum + NeighborAdvertisement(header) => pseudo_sum .add_2bytes([TYPE_NEIGHBOR_ADVERTISEMENT, 0]) .add_4bytes(header.to_bytes()), + Redirect => pseudo_sum + .add_2bytes([TYPE_REDIRECT_MESSAGE, 0]) + .add_4bytes([0; 4]), } .add_slice(payload) .ones_complement() @@ -583,8 +705,11 @@ impl Icmpv6Type { | ParameterProblem(_) | EchoRequest(_) | EchoReply(_) - | NeighbourSoliciation - | NeighbourAdvertisement { .. } => 8, + | RouterSolicitation + | RouterAdvertisement(_) + | NeighborSolicitation + | NeighborAdvertisement(_) + | Redirect => 8, } } @@ -605,8 +730,11 @@ impl Icmpv6Type { | ParameterProblem(_) | EchoRequest(_) | EchoReply(_) - | NeighbourSoliciation - | NeighbourAdvertisement { .. } => None, + | RouterSolicitation + | RouterAdvertisement(_) + | NeighborSolicitation + | NeighborAdvertisement(_) + | Redirect => None, } } } @@ -637,6 +765,11 @@ mod test { (TYPE_PARAMETER_PROBLEM, ParameterProblem(ParameterProblemHeader{ code: ParameterProblemCode::UnrecognizedNextHeader, pointer: u32::from_be_bytes(bytes5to8)})), (TYPE_ECHO_REQUEST, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))), (TYPE_ECHO_REPLY, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))), + (TYPE_ROUTER_SOLICITATION, RouterSolicitation), + (TYPE_ROUTER_ADVERTISEMENT, RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8))), + (TYPE_NEIGHBOR_SOLICITATION, NeighborSolicitation), + (TYPE_NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8))), + (TYPE_REDIRECT_MESSAGE, Redirect), ]; for test in type_u8_type_pair { assert_eq!(test.0, test.1.type_u8()); @@ -668,6 +801,11 @@ mod test { (0, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }), (0, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))), (0, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))), + (0, RouterSolicitation), + (0, RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8))), + (0, NeighborSolicitation), + (0, NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8))), + (0, Redirect), ]; for test in code_type_pair { assert_eq!(test.0, test.1.code_u8()); @@ -820,6 +958,25 @@ mod test { test_checksum_calc(EchoReply( IcmpEchoHeader::from_bytes(bytes5to8) )); + + // router solicitation + test_checksum_calc(RouterSolicitation); + + // router advertisement + test_checksum_calc(RouterAdvertisement( + RouterAdvertisementHeader::from_bytes(bytes5to8) + )); + + // neighbor solicitation + test_checksum_calc(NeighborSolicitation); + + // neighbor advertisement + test_checksum_calc(NeighborAdvertisement( + NeighborAdvertisementHeader::from_bytes(bytes5to8) + )); + + // redirect + test_checksum_calc(Redirect); } } } @@ -884,6 +1041,11 @@ mod test { }), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), + RouterSolicitation, + RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8)), + NeighborSolicitation, + NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)), + Redirect, ]; for hdr in len_8_hdrs { @@ -919,6 +1081,11 @@ mod test { }), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), + RouterSolicitation, + RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8)), + NeighborSolicitation, + NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)), + Redirect, ]; for hdr in variable_payload_headers { diff --git a/etherparse_proptest_generators/Cargo.toml b/etherparse_proptest_generators/Cargo.toml index 0967cacd..1a00b89b 100644 --- a/etherparse_proptest_generators/Cargo.toml +++ b/etherparse_proptest_generators/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "etherparse_proptest_generators" -version = "0.18.2" +version = "0.19.0" authors = ["Julian Schmid "] edition = "2021" repository = "https://github.com/JulianSchmid/etherparse"