Skip to content

Releases: JulianSchmid/etherparse

Corrected Fragmentation Handling, Additional IP Extension Headers Support & Qualitiy of Life Improvements

28 Nov 13:04
Compare
Choose a tag to compare

With this version the support for IPv6 gets extended and bugs in the parsing of fragmented packets as well as authentification headers are fixed. Additionally a bunch of performance improvements are included and new methods have been added (e.g. the method to_bytes for headers with static sizes).

It has been almost two years since the last update and I think it is fair to say that I underestimated the effort it would take to introduce partial support for IPv6 extension headers. As it was so long sice the last update a bunch of changes have piled on. This also means there are some breaking changes in this version.

The next versions will hopefully be smaller and contain some qualitiy of life improvements.

Special thanks to @Bren2010 for reporting the errors with fragmented packets.

Extension headers added to IpHeader & InternetSlice

With the added support for authentification headers (for both IPV4 and IPV6) and additional IPV6 extension headers support a place to store the results when parsing headers or slicing them had be chosen. After some though I decided to put the results into the enum values as a second argument.

So the signature of IpHeader has changed from

pub enum IpHeader {
    Version4(Ipv4Header),
    Version6(Ipv6Header)
}

to

pub enum IpHeader {
    Version4(Ipv4Header, Ipv4Extensions),
    Version6(Ipv6Header, Ipv6Extensions)
}

and the signature of InternetSlice has changed from

pub enum InternetSlice<'a> {
    Ipv4(Ipv4HeaderSlice<'a>),
    Ipv6(Ipv6HeaderSlice<'a>, [Option<(u8, Ipv6ExtensionHeaderSlice<'a>)>; IPV6_MAX_NUM_HEADER_EXTENSIONS]),
}

to

pub enum InternetSlice<'a> {
    Ipv4(Ipv4HeaderSlice<'a>, Ipv4ExtensionsSlice<'a>),
    Ipv6(Ipv6HeaderSlice<'a>, Ipv6ExtensionsSlice<'a>),
}

source() & destination() return static arrays:

Previously when slicing packets the the methods for accessing the source & destionation returned a slice reference:

pub fn source(&self) -> &'a [u8] {
    ...
}

which becomes a problem if you want to copy it to an actual header as the header structs expect an fixed-sized array. E.g. [u8;4] for IPv4:

Ipv4Header::new(
    ...
    // expects [u8;4], so we have to convert the slice into an fixed-sized array
    [
        slice.source()[0],
        slice.source()[1],
        slice.source()[2],
        slice.source()[3],
    ],
    ...
)

To get around this problem the return types of the source & destination methods have been changed to return fixed-sized arrays for Ipv4HeaderSlice, Ipv6HeaderSlice & Ethernet2HeaderSlice. E.g. for IPv4 the signature is now

pub fn source(&self) -> [u8;4] {
    ...
}

which enables you to simply pass address values to Ipv4Header::new:

Ipv4Header::new(
    ...
    // much better
    slice.source(),
    ...
)

Not only makes this change it easier to copy address values from a slice to a header, but it also should bring a minor performance improvements (together with other changes). Fixed-sized arrays don't require slice range checks when acessed and the arrays are small enough that they fit in one or two registers on 64bit systems.

UdpHeader::calc_checksum_ipv4* & UdpHeader::calc_checksum* now use a constant for the protocol field in the pseudo header

Previously checksum calculation functions for udp used a protocol value either given as an argument or taken from the ipv4 headers protocol field in it's checksum calculation. After having a closer look at RFC 768 and what Wireshark does, this seems to have been a mistake. Specifically when an authentifiction header is present between the ip header and the udp header. In this case ip_number::UDP (17) should be used and not the value of the ipv4 header protocol field (which will be ip_number::AUTH (51)).

To resolve this I changed the checksum calculation to always use ip_number::UDP and remove all arguments that allow the user to pass in the protocol number from the outside.

Which means

impl UdpHeader {
    pub fn calc_checksum_ipv4_raw(&self, source: [u8;4], destination: [u8;4], protocol: u8, payload: &[u8]) -> Result<u16, ValueError> {
        // ...
    }

looses the protocol argument

impl UdpHeader {

    pub fn calc_checksum_ipv4_raw(&self, source: [u8;4], destination: [u8;4], payload: &[u8]) -> Result<u16, ValueError> {

and

impl UdpHeader {
    pub fn with_ipv4_checksum(source_port: u16, destination_port: u16, ip_header: &Ipv4Header, payload: &[u8]) -> Result<UdpHeader, ValueError> {
        // ...
    }

    pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4Header, payload: &[u8]) -> Result<u16, ValueError> {
        // ....
    }

will no longer use ip_header.protocol in their checksum calculations.

General:

  • Corrected decoding & handling of authentification headers & encapsulating security payload for IPv6 packets.
  • Added support for authentifaction headers in IPv4 packets.
  • Corrected handling of fragmented packets. InternetSlice::from_* & PacketHeaders::from_* no longer try to decode packets that have been flaged as fragmented (IPv4 & IPv6). Thanks to @Bren2010 for making a PR & noticing the issue.
  • Added support for parsing "IPv6 Fragment Headers" & "Authentification Headers"

Fixed bugs:

  • The length field in authentification fields was assumed to be in 8 octet units (same as hop-by-hop options header & the routing header). This was incorrect, the length field is in 4 octet units and the code has been corrected to support this.
  • For the "Encapsulating Security Payload header" it was incorrectly assumed, that the basic build up is the same as for the other header extensions (with a next_header & header length field at the start of the header). Parsing of packets will now stop as soon as a "Encapsulating Security Payload header" is encountered.

Breaking API changes:

  • Renamed TcpOptionElement::Nop to TcpOptionElement::Noop
  • Renamed Ipv6ExtensionHeader to Ipv6RawExtensionHeader
  • Renamed Ipv6ExtensionHeaderSlice to Ipv6RawExtensionHeaderSlice
  • Reduced the list of supported headers as Ipv6RawExtensionHeader & Ipv6RawExtensionHeaderSlice to:
    • Hop-by-Hop Options Header
    • Routing Header
    • Destination Options Header
    • Mobility Header
    • Host Identity Protocol
    • Shim6 Header
  • Renamed IpTrafficClass::IPv6AuthenticationHeader to IpNumber::AuthenticationHeader.
  • Renamed IpTrafficClass::IPv6EncapSecurityPayload to IpNumber::EncapsulatingSecurityPayload
  • Renamed ReadError::VlanDoubleTaggingUnexpectedOuterTpid to ReadError::DoubleVlanOuterNonVlanEtherType
  • Moved the extensions out of the Ipv6Header[Slice] and into the PacketHeaders & SlicedPacket struct.
  • TcpOptionReadError::UnexpectedEndOfSlice changed from a single value

This change had been a long time coming. Originally I coupled the IPv6 header extensions to the ipv6 header under the assumption that they only exist in IPv6. But this was not correct, the authentication header and encapsulating security payload are present in IPv6 as well as IPv4. So seperating this form IPv6 made sense.

  • Ipv6ExtensionHeader was extended with a slice pointing to the data of the header
  • Moved TCP_OPTION_ID_* contants into a new module tcp_options::KIND_* (the old constants still present but marked as deprecated).
  • Return type of Ethernet2HeaderSlice::{destination, source} changed to [u8;6] (previously &'a [u8])

API changes with deprecation warning:

The following changes will cause a deprecation warning:

  • Renamed IpTrafficClass to IpNumber. Traffic class was just the wrong name and confusing as there is a traffic class field in IPv6 headers.
  • Renamed read_from_slice methods to from_slice.

Added std::error::Error & std::fmt::Display implementations for errors

09 Dec 21:52
Compare
Choose a tag to compare
  • ReadError, WriteError & ValueError now have std::error::Error & std::fmt::Display implementations
  • Removed unused enum values ErrorField::{Ipv4HeaderLength, TcpDataOffset}

Changes for future compatibility & updated dependencies

30 Oct 22:42
Compare
Choose a tag to compare
  • Added #7 for future compatibility
  • Updated byteorder to 1.3.2 and test dependency proptest to 0.9.4

Corrected UDP Header Documentation & Updated dependencies

13 May 20:59
Compare
Choose a tag to compare
  • Merged #5 (Fix UDP header function docstrings)
  • Updated dependencies to latest version
    • byteorder (from 1.2.7 to 1.3.1)
    • proptest (used in tests only from 0.8.7 to 0.9.3)
  • Adapted test data generators to new proptest version

Missing IPv6 Header Extensions, IPv4 Options & Parsing from the IP Layer downwards

05 Jan 16:08
Compare
Choose a tag to compare

Overview

  • Previously missing IPv6 extension header id's have been added and are now correctly handled by the skipping & slicing functions.
  • Functions have been added to SlicedPacket & PacketHeader & PacketBuilder that allow the slicing, reading, and generation of packets starting from the ip layer downwards (thanks to @razorheadfx ).
  • Additionally the IPv4 header options, that were previously passed in as an argument, have now moved into the Ipv4Header struct itself. This sadly means there are some breaking changes.
  • All headers now also have methods for slicing packets directly from slices.

Ipv4Header Changes in Detail:

Breaking changes for Ipv4Header:

  • Ipv4Header now contains two private fields: "options_buffer" & "options_len". As such you can no longer directly initialize header fields directly. Instead you will have to use Default::default() or Ipv4Header::new() and set fields afterwords.
  • Renamed "set_payload_and_options_length" to "set_payload_len" as the options are now present in the header.
  • "new" method changes:
    • No longer returns a result (use check_ranges if you want to verfy if your values can be used)
    • Takes the payload_len as a u16 (previously usize, use set_payload_len if you want to use usize)
  • "write" & "write_raw" no longer take a slice to the options. The options are now present in the header, use the "set_options" method if you want to write Ipv4Header options.

Additional Ipv4Header changes:

  • Added method "ihl" for reading the internet header length
  • Added method "options" to get a slice of to the options
  • Added method "header_len" to get the bytes length of the header + options
  • Added method "total_len" to get the bytes length of ip header + payload
  • Added method "set_options" to set the options of the header
  • Added method "check_ranges" for checking the ranges of the values in the header (used in the write methods).

Ipv4HeaderSlice changes:

  • "from_slice" added check of the total length (no sizes smaller then the header itsel allowed)

Bug fix: Replaced non guaranteed write with guaranteed write_all when writing tcp options.

14 Nov 06:31
Compare
Choose a tag to compare

tcp.rs: Bug fix: Replaced non guaranteed write with guaranteed write_all when writing tcp options.

PacketBuilder TCP support & packet filters moved into "packet_filter" mod

28 Oct 12:54
Compare
Choose a tag to compare

Added TCP support to the packet builder (check out the example in examples/write_tcp.rs).

Moved the filter into a separate module, as they are not part of the core functionality.

TcpHeader.set_options padding corrected & rudimentary packet filters

15 Oct 20:13
Compare
Choose a tag to compare
  • TcpHeader.set_options: Use zeros only for padding when not aligned (thanks @agend )
  • Added rudimentary packet filter

TCP Support and Slice moved into separate structs

02 Aug 05:57
Compare
Choose a tag to compare
  • TCP support for fully decoding it and handling as a slice
  • TCP options writing & parsing support (iterator)
  • Removed PacketSlice struct and moved implementations into separate structs (e.g. TcpHeaderSlice)

Added automatic slicing & parsing of entire packets and headers

15 Jul 17:04
Compare
Choose a tag to compare

This version adds PacketSlices for the different headers and the automatic parsing & slicing of entire packets. Additionally tests have been added that test all known compositions of packets for automatic parsing & slicing packets. Also some bugs have been fixed related to prematurely ending packets.